javascripttypescriptangularstatic-htmlserver-side-rendering

The selector "app" did not match any elements


I m using angular2-universal for server side rendering and generating static HTML.

so far my static HTML is generated & component is also getting rendered. but in my browser console its showing this error.

EXCEPTION: The selector "app" did not match any elements

I have used following code for this purpose:

1.index.html

<!doctype html>
<html lang="en">

<head>
    <link rel="icon" href="data:;base64,iVBORw0KGgo=">
    <link href="\node_modules\bootstrap\dist\css\bootstrap.min.css" rel="stylesheet" />
    <link href="\node_modules\bootstrap\dist\css\style.css" rel="stylesheet" />
    <script src="/node_modules/es6-shim/es6-shim.js"></script>
    <script src="/node_modules/es6-promise/dist/es6-promise.js"></script>
    <script src="/node_modules/reflect-metadata/Reflect.js"></script>
    <script src="/node_modules/zone.js/dist/zone-microtask.js"></script>
    <script src="/node_modules/zone.js/dist/long-stack-trace-zone.js"></script>
    <script src="/dist/client/bundle.js"></script>    
</head>
<body>
    <app>Loading....</app>    
</body>
</html>

2.client.ts

import {bootstrap} from 'angular2/platform/browser';
import {HTTP_PROVIDERS} from 'angular2/http';
import 'rxjs/add/operator/map';
import {App} from './app/app.component';

bootstrap(App,[HTTP_PROVIDERS,]);

3.server.ts

import * as path from 'path';
import * as express from 'express';
import * as universal from 'angular2-universal-preview';

// Angular 2
import {App} from './app/app.component';


let app = express();
let root = path.join(path.resolve(__dirname, '../'));

// Express View
app.engine('.ng2.html', universal.ng2engine);
app.set('views', __dirname);
app.set('view engine', 'ng2.html');

// Serve static files
app.use(express.static(root));
var i = 0;
// Routes
app.use('/', (req, res) => {
    res.render('index_new', {App}, function(err,html){
    console.log("------------------My HTML ---------\n"+(i++));
    console.log(html);
    console.log("------------------My HTML ----------");
    res.send(html);
   });
});

// Server
app.listen(3000, () => {
  console.log('Listen on http://localhost:3000');
});

4.app.component.ts

import {Component} from 'angular2/core';
import {NgSwitch, NgSwitchWhen, NgSwitchDefault,NgFor} from 'angular2/common';
import {Http, Response} from 'angular2/http';
import {Observable} from 'rxjs/Rx';

@Component({
    selector: 'app', 
    template: `
            <div *ngFor="#comp of record">
            <div class="col-sm-4 divDynamic">
                <table style="width:100%;">
                    <tr style="background-color:#B8B8B8">
                        <th>{{comp.data[comp.mappings[0].mapAttr]}}</th>
                    </tr>
                    <tr>
                        <td>&nbsp;</td>
                    </tr>
                </table>
                <br/>
            </div>
            <div class="col-sm-4 divDynamic">
                <table  style="width:100%;">
                    <tr style="background-color:#B8B8B8">
                        <th>{{comp.data[comp.mappings[1].mapAttr]}}</th>
                    </tr>
                    <tr>
                        <td>&nbsp;</td>
                    </tr>
                </table>
                <br/>
            </div>        
            <div class="col-sm-4 spec" style="float:right;">        
                <table  style="width:100%;">
                    <tr style="background-color:#f5821f;color:white;">
                        <th>{{comp.data[comp.mappings[2].mapAttr]}}</th>
                    </tr>
                    <tr>
                        <td style="border:none;text-align:left;">{{comp.data[comp.mappings[16].mapAttr]}}</td>
                    </tr>
                    <tr>
                        <td style="border:none;text-align:left;">{{comp.data[comp.mappings[15].mapAttr]}}</td>
                    </tr>
                    <tr>
                        <td style="border:none;text-align:left;">{{comp.data[comp.mappings[17].mapAttr]}}</td>
                    </tr>
                </table>
                <br/>
            </div>
            <div class="col-sm-8 spec">       
                <table style="width:100%;">
                    <tr>
                        <td rowspan="2" style="background-color:#f5821f;color:white;" class="col-xs-2 ">{{comp.data[comp.mappings[3].mapAttr]}}</td>
                        <th>{{comp.data[comp.mappings[7].mapAttr]}}</th>
                        <th>{{comp.data[comp.mappings[7].mapAttr]}}</th>
                        <th>{{comp.data[comp.mappings[7].mapAttr]}}</th>
                        <th>{{comp.data[comp.mappings[7].mapAttr]}}</th>
                    </tr>
                    <tr>
                        <td>&nbsp;</td>
                        <td>&nbsp;</td>
                        <td>&nbsp;</td>
                        <td>&nbsp;</td>
                    </tr>
                </table>
                <br/>
            </div>
            <div class="col-sm-8 divDynamic">
                <table style="width:100%;">
                    <tr>
                        <td rowspan="2" style="background-color:#B8B8B8;" class="col-xs-2 ">{{comp.data[comp.mappings[4].mapAttr]}}</td>
                        <th>{{comp.data[comp.mappings[11].mapAttr]}}</th>
                        <th>{{comp.data[comp.mappings[12].mapAttr]}}</th>
                    </tr>
                    <tr>
                        <td>&nbsp;</td>
                        <td>&nbsp;</td>
                    </tr>
                </table>
                <br/>
            </div>
            <div class="col-sm-12 divDynamic">
                <table style="width:100%;">
                    <tr>
                        <td rowspan="2" style="background-color:#B8B8B8;" class="col-xs-2 ">{{comp.data[comp.mappings[5].mapAttr]}}</td>
                        <th>{{comp.data[comp.mappings[13].mapAttr]}}</th>
                        <th>{{comp.data[comp.mappings[14].mapAttr]}}</th>
                    </tr>
                    <tr>
                        <td>&nbsp;</td>
                        <td>&nbsp;</td>
                    </tr>
                </table>
                <br/>
            </div>
            <div class="col-sm-12" style="color:#a41c2b;border:solid #a41c2b 2px;">
                {{comp.data[comp.mappings[6].mapAttr]}}        
            </div>
        </div>   
        `
 })
export class App{
    public record = [{
        "styling": {
            "overall": {}
        },
        "data": {
            "statementDate": "Statement Date",
            "minAmtDue": "Minimum Amount Due",
            "totalAmtDue": "Your Total Amount Due",
            "stmtSummary": "Statement Summary",
            "creditSummary": "Credit Summary",
            "rewards": "REWARDS",
            "bottomPara": "Convert your Big shopping bills into small, manageable EMI's! If any of the below transaction is highlighted,you may login to Internet Banking and convert into EMI now ! TnC apply.",
            "prevBalance": "Previous Balance",
            "purchases": "Purchases/ Charges",
            "cashAdv": "Cash Advances",
            "payments": "Payments/ Credits",
            "creditLimit": "Credit Limit",
            "creditAvail": "Available Credit",
            "pointsEarned": "Points Earned",
            "payback": "Points Transferred to PAYBACK(Acc:",
            "dueDate": "Due Date:",
            "Amt": "Rs.",
            "interest": "Interest will be levied if Total Amount Due is not paid"
        },
        "mappings": [
            {
                "mapAttr": "statementDate"
            },
            {
                "mapAttr": "minAmtDue"
            },
            {
                "mapAttr": "totalAmtDue"
            },
            {
                "mapAttr": "stmtSummary"
            },
            {
                "mapAttr": "creditSummary"
            },
            {
                "mapAttr": "rewards"
            },
            {
                "mapAttr": "bottomPara"
            },
            {
                "mapAttr": "prevBalance"
            },
            {
                "mapAttr": "purchases"
            },
            {
                "mapAttr": "cashAdv"
            },
            {
                "mapAttr": "payments"
            },
            {
                "mapAttr": "creditLimit"
            },
            {
                "mapAttr": "creditAvail"
            },
            {
                "mapAttr": "pointsEarned"
            },
            {
                "mapAttr": "payback"
            },
            {
                "mapAttr": "dueDate"
            },
            {
                "mapAttr": "Amt"
            },
            {
                "mapAttr": "interest"
            }
        ]
    }
    ];
}


Solution

  • After looking at this code I realized that it bootstrapping app on client side too.

    After deleting below line from client.ts it works fine.

    bootstrap(App,[HTTP_PROVIDERS,]);
    

    after I removed this line from client.ts , the application didn't produce any errors. but I am not sure why universal-starter people had used this line in first place if they are using server-side-rendering.

    what i am understanding from this is , application tried to load the component two times,

    from client side & server side respectively.

    So it rendered server side component but failed to find selector for client side component.