Angular 2 child element attribute not working, use input decorator

When defining a re-usable modal container, I could not get the ‘title’ attribute to appear when set on the parent template’s use of the component.

I was using a modal with a map sibling, as

template: '<div id="map-canvas"></div><modal-container title="Section Notes"></modal-container>'

I figured out the child component needed to use the input decorator on the ‘title’ property. So in modal-container.component.ts, in the export class ModalContainerComponent definition

title: string;

became

@Input() title: string;

and it worked.

See https://angular.io/docs/ts/latest/cookbook/component-communication.html at “Pass data from parent to child with input binding”

Using php with Angular 2 and Laravel

I’ve been using Angular 1.0 for the last few projects, but I wanted to get familiar with Angular 2. I found the process of getting up and running with php a bit annoying.

SUMMARY: With php, you can use the ‘5 Minute Quickstart’ tutorial google offers with some adjustments to asset locations, config paths, node packages, and automated tasks (most notably the typescript transpilation process) to get setup with Laravel and Angular 2. Google uses a node app, and a lite node server. I wanted to avoid this and use php and laravel to deploy assets, etc. and use Angular 2. Conceptually:

  1. Make sure you understand which packages you will need. The best would be just to copy the ones from the Angular 2 google tutorial, though not quite all of them will be used (we will let Laravel handle the typescript transpilation).
  2. Make sure you understand where the hell all the files should go. You need to change google’s root level file structure of the example node app to a php laravel app where public assets reside in the nested ‘public’ folder within the root app. Paths in the files need to be adjusted accordingly.
  3. Make sure you understand how to adjust the automated tasks in the gulp file, and in the laravel-elixir-typescript node package. This is mainly about how to get your typescript files to end up in the right place when they are compiled to javascript.

See https://angular.io/docs/ts/latest/quickstart.html

I’ve tried to explain my experience in a way that will make things easier for someone trying to setup any general php app with angular 2 at any point in the near future. The packages, versions, and syntax of the code may vary in the future, but if you understand what changes you need to make conceptually, it will hopefully be easy to use Google’s tutorials and documentation for the next while. My experience was based on the somewhat murky guide at https://www.codetutorial.io/laravel-5-and-angular-2-beta-setup. The video is at https://www.youtube.com/watch?v=dohhsLSbkYA. I found I needed some clarifications on paths, etc. and that the packages used and versions were outdated or unexplained. I tweaked the tutorial found at https://angular.io/docs/ts/latest/quickstart.html. I was using Laravel 5.2.43. I’ve tried to follow the steps in the google tutorial.

Step 1: Create and configure the Laravel and Angular 2 project

I took a laravel project and figured out where to put all the files from the google Angular 2 tutorial. Some files from the tutorial could just go in the root of the app. The following things need to be adjusted:

a) Create a laravel project, and change into that directory. Mine is called laravel_angular2.

laravel new laravel_angular2
cd laravel_angular2

I’ve gotten used to setting up a virtual host and getting the app and and running with php and apache. Hopefully you’ve already made it to this point.

b) Package definition and config files. Copy the typings.json file into the root of your project. Ignore the tsconfig.json file, as we will use laravel an gulp to handle typescript transpiling and serving the app. Put the systemjs.config.js file in the /public folder of laravel. UPDATE THE PATHS in this file to point to / instead of /node_modules, your public folder. The package.json file needs to be merged with existing laravel one. You can essentially copy the dependencies and devDependencies portions of the tutorial package.json file into the package.json file in the root of your laravel app. You can tweak the packages, perhaps remove the lite server and typescript stuff that google includes. As for me, I just left all the packages google indicated in there. I have yet to figure out a good way to determine, when cloning projects, how the hell to figure out which packages are unnecessary, and how to tell which should just go in the devDependencies section when the list gets real long and full of unfamiliar packages. The scripts and other sections of the tutorial package.json can be ignored since, again, we will use laravel an gulp to handle typescript transpiling and serving the app.

c) Install packages. npm install should work fine. Somehow I always screw the packages up and get error messages, but nothing helpful on how to avoid this comes to mind. Hopefully you only get some warnings. I could not get npm run typings install to work, as per the tutorial. I don’t know if this is because we removed it from the scripts section of the package.json file. You can try adding this section and run the command. I suspect it had more to do with the fact that the console wanted a cli package for typings. Do npm install typings --global to get the global cli package, then typings install. We need to deal with the typings folder and move it into public. I didn’t get errors in my quickstart app without moving the typings folder, but it should be moved to the public folder. So, move the ‘typings’ folder into /public. You could probably use a gulp task command to do this. You could also figure out how the hell to adjust google’s code to put this typings folder in public automatically. I never got to that point. The typings.json config file made no sense to me and I don’t know any of the conventions on using that package. I actually don’t even know what the typings folder is used for, but figured it should go in the public folder.

Ignore the ‘Helpful scripts’ section, as we will use laravel and gulp to run the typescript transpiling and the server.

Steps 2-4: Our first Angular 2 component, etc.

Instead of creating an ‘app’ root folder, make a folder ‘/resources/assets/typescript/app’ to house your typescript files. They will be transpiled to the /public/app folder later. Put all the files mentioned in the tutorial in steps 2-4 there.

Step 5.0: Making sure our Angular 2 typescript transpiles using Laravel and Gulp

I couldn’t get google’s typescript transpiler to work easily, and I wanted to use the one included in the laravel packages. The tricky part about this is to avoid jamming on the javascript into a single app.js file. There are 2 ways I’ve done this. The second way is demonstrated in the tutorial on codetutorial.io (and the youtube video), but it involves modifying a node package in the node_modules folder, which is fragile. In my opinion.

  1. Use the gulp typescript library and make a task for transpiling the javascript from typescript.
    var ts = require('gulp-typescript');
    ...
    gulp.task('typescript', function(){
      var assetPath = './' + elixir.config.assetsPath;
      var search = '/typescript/**';
      var options = {
          // If you use ES5, see http://stackoverflow.com/questions/35660498/angular-2-cant-find-promise-map-set-and-iterator
          "target": "ES6",
          "module": "system",
          "moduleResolution": "node",
          "sourceMap": true,
          "emitDecoratorMetadata": true,
          "experimentalDecorators": true,
          "removeComments": false,
          "noImplicitAny": false,
      };
      var outputFolder = 'public';
      return gulp.src(assetPath + search)
        .pipe(ts(options))
        .pipe(gulp.dest(outputFolder))
    });
    
    ... //in the elixir function
        mix.task('typescript', 'resources/assets/typescript/**'); 
    • This is the weird custom step I found useful. It deals with getting the transpiling to work using the npm package elixir-typescript. This involves tweaking the package code, and will not be committed to the git app as it involves editing the node_modules folder. I don’t know if there is a better way to do this, but this idea comes from the aforementioned tutorial. Add the elixir-transcript package. Version 1.1.2: npm install elixir-typescript@1.1.2 I haven’t added this to the package.json file, as we are editing the package manually, but you could add it. Be aware that on certain npm actions, the required update that makes our transpiling work may be overwritten and break.
    • In the file /node_modules/elixir-typescript/index.js, comment out the concat line. //.pipe(concat(outputFileName)). This was on line 28 for me. This is so that when the typescript files are transpiled, they don’t all get mashed into one app.js file. Angular 2 would not like this. Every time you do npm install, you may have to redo this!
    • add the following to your gulp file. The first line was already there for me:<code>

      var elixir = require('laravel-elixir');
      var elixirTypscript = require('elixir-typescript');
      ...
      elixir(function(mix) {
      mix.typescript('app.js','public/app','/typescript/**/*.ts',{
        // If you use ES5, see http://stackoverflow.com/questions/35660498/angular-2-cant-find-promise-map-set-and-iterator
        "target": "ES6",
        "module": "system",
        "moduleResolution": "node",
        "sourceMap": true,
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "removeComments": false,
        "noImplicitAny": false,
      });

      Run gulp typescript to make sure it works. There should now be javascript files in /public/app. The paths here are weird and took me some tinkering to figure out. More on this later. These 3 steps were definitely the trickiest part of the whole process. It’s more about if you’re keen on learning how to use typescript with your php projects, as similar steps would probably need to be used in any tool that uses typescript on the frontend.

Step 5

Setup the welcome/index/homepage of your Laravel app to load the Angular 2 app. Instead of making an index.html as per the tutorial, update your home view, or whatever you want to run the angular 2 app, to use the html that is in the tutorial. We will need to modify the url paths since the packages used by Angular 2 reside in node_modules folder, which is not in /public. We also need to copy the used packages into the /public folder so we can reference them from the script tags. We will accomplish this by:

  1. Input the html, etc. from the tutorial. In my /resources/views/welcome.blade.php file I added Loading... in the content div. Copy the script tags from the tutorial into the head section.
  2. Add the following commands to the Laravel gulp file in the elixir function:<code>

    mix.copy('node_modules/core-js', 'public/core-js');
    mix.copy('node_modules/reflect-metadata', 'public/reflect-metadata');
    mix.copy('node_modules/zone.js/dist/zone.js', 'public/zone.js/dist/zone.js');
    mix.copy('node_modules/systemjs', 'public/systemjs');

    The following 3 files are also necessary for the framework.

    mix.copy('node_modules/@angular', 'public/@angular');
    mix.copy('node_modules/angular2-in-memory-web-api', 'public/angular2-in-memory-web-api');
    mix.copy('node_modules/rxjs', 'public/rxjs');
  3. Remove references to node_modules from the script includes in your view file. For example, ‘node_modules/core-js’ should just be ‘core-js’. I edited my /resources/views/welcome.blade.php and removed all the ‘node_modules’ from the script tags. The systemjs.config.js file should already be in the public folder from Step 1. Again, note that you should remove reference to the ‘node_modules’ path in this systemjs.config.js.And with that, run gulp, hit your Laravel app’s url,and the quickstart app should load! You’ll probably want to add a gulp task to monitor changes to your typescript. And this is usually the point in a web post where I’m like ‘What the hell, this isn’t working and you seem to be missing some explanations.’ As I think of them, I’ll try and stay current and add in more explanations and pitfalls to avoid this.

jQuery checking if a selector worked or is valid

So if a jQuery selector failed to match it used to return the document at the root level of the DOM. Now it will return an empty set. What is an empty set? If you do var test = jQuery(); you’ll get the thing — an empty jQuery wrapper object that appears around every selected element when using jQuery selectors.

So, if you do $(‘#bogusId)’, you should get a jQuery object with a length of zero. I think that is the best way I have seen to tell if nothing was matched, just check for a length of zero on the result of calling something that doesn’t match.

if($(‘#bogusID’).length ==0)

OR

var myjQueryElement = $(‘#bogusID’);
if(myjQueryElement.length == 0 )

You can also use size()

See http://api.jquery.com/jQuery/

‘As of jQuery 1.4, calling the jQuery() method with no arguments returns an empty jQuery set. In previous versions of jQuery, this would return a set containing the document node.’

jQuery Dialog not showing content after being hidden

So the autoOpen:false on the jQuery dialog instantiation doesn’t hide the div fast enough sometimes. You can skirt this by setting the display to none (display:none) on the dialog div you made, but then when you actually do load up the dialog after someone clicks an anchor, or whatever, the content of the div doesn’t show up because the child divs have all inherited the display:none;

Just call the show method after the dialog load to skirt this.

$(“#detailPopin”).dialog(‘open’);
$(“#detailPopin”).show();

Error: ‘jQuery.event.special[…].teardown’ is null or not an object

Apparently, when the page is left, jQuery tries to remove event listeners. When this happens, the jQuery.event.special[type].teardown is null or undefined in a line of the jQuery source. This appears to be a dialog error, or caused by the dialog somehow. The jQuery error is found on line 1948 using the latest release, 1.2.6. (For me this is line 1955 but my jQuery has been a little modified).

I added the following to the if on line 1948 – || !jQuery.event.special[type].teardown. This fixes the jQuery error on line 1948.

if ( !jQuery.event.special[type] || jQuery.event.special[type].teardown.call(elem)
=== false ) {

becomes

if ( !jQuery.event.special[type] || !jQuery.event.special[type].teardown
|| jQuery.event.special[type].teardown.call(elem) === false ) {

This makes it so if the teardown function is undefined, an error won’t occur.

I am fairly certain that this fix doesn’t break anything else, but no guarantees. I didn’t spend a lot of time going into what elem.removeEventListener or elem.detatchEvent does because it looks like the eventListeners are not very consequential. Maybe a little bit more overhead. All I know is when I added the extra if, things worked great from that point on. And what I call a jQuery error could very well be an IE error, a user error (me, of course), etc. I happen to love jQuery.

jQuery Dialog error in IE7. Runtime error: invalid argument

So if you’re using jQuery UI dialogs, you may run into issues with IE 7. Apparently the value of a usually ‘undefined’ javascript variable can get set to ‘NaNpx’ in IE7. I had to change the jQuery base code to get a dialog ui component to work.

I was getting the following runtime error: “Line: 1102 Error: invalid argument.”

This error comes from code around line 1101 in jquery-1-2-6.js.

So I think my version of jQuery had already been modified or comes from an older or different source. My code follows, but the most recent 1-2-6 jQuery script has code that is like the comment Dom left (thanks – that code looks more like the most recent release of jQuery).


/* MODIFIED 7/31/08 BY NATE
* WAS THIS, but IE7 was throwing an error
if ( value != undefined )
elem[ name ] = value;
*/
if ( value != 'NaNpx' && value != undefined)
elem[ name ] = value; //NATE - Sometimes the value would come up as NaNpx in
IE and causes an error

jquery runtime error for dialog

With Internet Explorer, Javascript setAttribute(‘style’, ‘something’) doesn’t work in IE

If you try and set the style attribute of a DOM element with Javascript, the following code WON’T WORK IN IE:

yourelement.setAttribute('style','left:150px; top:150px;');

Instead use:

yourelement.style.cssText = 'left:150px; top:150px;';

Thanks to Rob on this page

http://www.quirksmode.org/bugreports/archives/2005/03/setAttribute_does_not_work_in_IE_when_used_with_th.html

http://blog.throbs.net/

Beware the affect of the base href= on AJAX requests, and IE. Permission denied to call XMLHttpRequest open method

The base href effect on XMLHtttpRequest.open (in IE)

Imagine that. If your base href is set to something weird, the ajax requests will get Access errors indicating “Permission Denied to call method yourXMLHttpRequestObject.open” Errors! But only in IE.

In IE, with AJAX requests, your url root must match your base href. base ref=”http://www.mydomain.com should be the same as the base url in xmlHttp.open(“GET”, “http://www.mydomain.com/MyRemoteServices/MyRemoteOrderService.cfc?
method=getOrderInfoXML&userid=1234”, true), or you can use relative urls.

Example I had:


//The XMLHttpRequest is named xmlHttp here, xmlHttp = new XMLHttpRequest()
//If the base href is set to something other than the server root, the AJAX request
//thinks the request is coming from a different server, or something.
//Permission denied to call XMLHttp.open with the following
//SET IN HTML HEAD <base href="http://www.mydomain.com/index.cfm?event=myOrder" /> - NOT HTTPS!
xmlHttp.open("GET", "https://mydomain.com/MyRemoteServices/MyRemoteOrderService.cfc?
method=getOrderInfoXML&userid=1234" , true);

//Now when the base href is set to the server root, no problems
//SET IN HTML HEAD <base href="https://www.mydomain.com/"> - HTTP!
xmlHttp.open("GET", "https://mydomain.com/MyRemoteServices/MyRemoteOrderService.cfc?
method=getOrderInfoXML&userid=1234" , true);

Issues when www isn’t mapped to your home url

Another issue that can happen with access denied errors with your AJAX request is when your www doesn’t resolve to the same url as your root. I.E. http://www.yourdomain.com isn’t the same dns entry as http://ourdomain.com, even if they both eventually go to the same server ip.

Error: Happens when your session is in http://www.yourdomain.com and your AJAX request points to http://yourdomain.com (if your www subdomain doesn’t resolve to http://yourdomain.com).


//I'm on a http:://www.mydomain.com page, and I get an error with this line
xmlHttp.open("GET", "http://mydomain.com/MyRemoteServices/MyRemoteOrderService.cfc?
method=getOrderInfoXML&userid=1234" , true);
// I'm on a http:://mydomain.com page, this line will work
xmlHttp.open("GET", "http://mydomain.com/MyRemoteServices/MyRemoteOrderService.cfc?
method=getOrderInfoXML&userid=1234" , true);