generator-ionic icon indicating copy to clipboard operation
generator-ionic copied to clipboard

HTML5 Mode true after reload/refresh Cannot GET /app/welcome

Open anjum121 opened this issue 9 years ago • 3 comments

I have read many posts and googled it a lot, but couldn't able to work HTML5 mode true in my app. when i grunt serve app is working fine with all navigation etc, but when i refresh i am getting error Cannot GET /app/welcome

any help would be great

PS. i have checked this http://stackoverflow.com/questions/16569841/angularjs-html5-mode-reloading-the-page-gives-wrong-get-request?lq=1

anjum121 avatar Mar 01 '15 14:03 anjum121

You need to enable URL rewrite in the server to get this to work properly...

Setting of url rewrite is different for different servers....

johntomcy avatar Jul 05 '15 19:07 johntomcy

@johntomcy I do understand that server setting my differ, but when i am running locally, is there a way to get it work.

I am really sorry for posting full Grunt file, but i am helpless, Please guide me where i am doing wrong.

Thanks

// Generated on 2015-02-16 using generator-ionic 0.7.0
'use strict';

var _ = require('lodash');
var path = require('path');
var cordovaCli = require('cordova');
var spawn = process.platform === 'win32' ? require('win-spawn') : require('child_process').spawn;

var modRewrite = require('connect-modrewrite');
var mountFolder = function(connect, dir){
      return connect.static(require('path').resolve(dir));
};

 module.exports = function(grunt){
// Load grunt tasks automatically
require('load-grunt-tasks')(grunt);

// Time how long tasks take. Can help when optimizing build times
require('time-grunt')(grunt);

// Define the configuration for all the tasks
grunt.initConfig({

    // Project account
    yeoman: {
        // configurable paths
        app: 'app',
        scripts: 'scripts',
        fonts: 'fonts',
        styles: 'styles',
        images: 'images',
        test: 'test',
        dist: 'www'
    },


    // Watches files for changes and runs tasks based on the changed files
    watch: {
        bower: {
            files: ['bower.json'],
            tasks: ['wiredep', 'newer:copy:app']
        },
        html: {
            files: ['<%= yeoman.app %>/**/*.html'],
            tasks: ['newer:copy:app']
        },
        js: {
            files: ['<%= yeoman.app %>/<%= yeoman.scripts %>/**/*.js'],
            tasks: ['newer:copy:app', 'newer:jshint:all']
        },
        compass: {
            files: ['<%= yeoman.app %>/<%= yeoman.styles %>/**/*.{scss,sass}'],
            tasks: ['compass:server', 'autoprefixer', 'newer:copy:tmp']
        }

    },

    // The actual grunt server account
    connect: {
        options: {
            port: 9000,
            livereload: 35729,
            // Change this to '0.0.0.0' to access the server from outside.
            hostname: '0.0.0.0'
        },
        server: {
            options: {
                middleware: function (connect) {
                    return [
                        modRewrite (['!\\.html|\\.js|\\.svg|\\.css|\\.png|\\.jpg$ /index.html [L]']),
                        mountFolder(connect, 'app')
                    ];
                }
            }
        },
        livereload: {
            options: {
                open: true,
                base: [
                    '.tmp',
                    '<%= yeoman.app %>'
                ],
                // MODIFIED: Add this middleware configuration
                middleware: function(connect, options){
                    var middlewares = [];

                    middlewares.push(modRewrite(['^[^\\.]*$ /index.html [L]'])); //Matches everything that does not contain a '.' (period)
                    options.base.forEach(function(base){
                        middlewares.push(connect.static(base));
                    });
                    return middlewares;
                }
            }
        },
        dist: {
            options: {
                base: '<%= yeoman.dist %>'
            }
        },
        coverage: {
            options: {
                port: 9002,
                open: true,
                base: ['coverage']
            }
        }
    },

    // Make sure code styles are up to par and there are no obvious mistakes
    jshint: {
        options: {
            jshintrc: '.jshintrc',
            reporter: require('jshint-stylish')
        },
        all: [
            'Gruntfile.js',
            '<%= yeoman.app %>/<%= yeoman.scripts %>/**/*.js'
        ],
        test: {
            options: {
                jshintrc: 'test/.jshintrc'
            },
            src: ['test/unit/**/*.js']
        }
    },

    // Empties folders to start fresh
    clean: {
        dist: {
            files: [{
                dot: true,
                src: [
                    '.temp',
                    '<%= yeoman.dist %>/*',
                    '!<%= yeoman.dist %>/.git*'
                ]
            }
            ]
        },
        server: '.temp'
    },

    autoprefixer: {
        options: {
            browsers: ['last 1 version']
        },
        dist: {
            files: [{
                expand: true,
                cwd: '.temp/<%= yeoman.styles %>/',
                src: '{,*/}*.css',
                dest: '.temp/<%= yeoman.styles %>/'
            }
            ]
        }
    },

    // Automatically inject Bower components into the app
    wiredep: {
        app: {
            src: ['<%= yeoman.app %>/index.html'],
            ignorePath: /\.\.\//
        },
        sass: {
            src: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
            ignorePath: /(\.\.\/){1,2}lib\//
        }
    },

    // Compiles Sass to CSS and generates necessary files if requested
    compass: {
        options: {
            sassDir: '<%= yeoman.app %>/<%= yeoman.styles %>',
            cssDir: '.temp/<%= yeoman.styles %>',
            generatedImagesDir: '.temp/<%= yeoman.images %>/generated',
            imagesDir: '<%= yeoman.app %>/<%= yeoman.images %>',
            javascriptsDir: '<%= yeoman.app %>/<%= yeoman.scripts %>',
            fontsDir: '<%= yeoman.app %>/<%= yeoman.styles %>/fonts',
            importPath: '<%= yeoman.app %>/bower_components',
            httpImagesPath: '/<%= yeoman.images %>',
            httpGeneratedImagesPath: '/<%= yeoman.images %>/generated',
            httpFontsPath: '/<%= yeoman.styles %>/fonts',
            relativeAssets: false,
            assetCacheBuster: false,
            raw: 'Sass::Script::Number.precision = 10\n'
        },
        dist: {
            options: {
                generatedImagesDir: '<%= yeoman.dist %>/<%= yeoman.images %>/generated'
            }
        },
        server: {
            options: {
                debugInfo: true
            }
        }
    },

    // Reads HTML for usemin blocks to enable smart builds that automatically
    // concat, minify and revision files. Creates configurations in memory so
    // additional tasks can operate on them
    useminPrepare: {
        html: '<%= yeoman.app %>/index.html',
        options: {
            dest: '<%= yeoman.dist %>',
            staging: '.temp',
            flow: {
                html: {
                    steps: {
                        js: ['concat', 'uglifyjs'],
                        css: ['cssmin']
                    },
                    post: {}
                }
            }
        }
    },

    // Performs rewrites based on the useminPrepare configuration
    usemin: {
        html: ['<%= yeoman.dist %>/{,*/}{,*/}{,*/}*.html'],
        css: ['<%= yeoman.dist %>/{,*/}/{,*/}{,*/}*.css'],
        options: {
            assetsDirs: [
                '<%= yeoman.dist %>',
                '<%= yeoman.dist %>/images'
            ]
        }
    },

    // The following *-min tasks produce minified files in the dist folder
    cssmin: {
        options: {
            //root: '<%= yeoman.app %>',
            noRebase: true
        },
        dist: {
            files: [
                {
                    expand: true,
                    cwd: '.temp/<%= yeoman.styles %>',
                    src: ['*.css'],
                    dest: '.temp/<%= yeoman.styles %>'
                }
            ],
            noRebase: true
        }
    },
    htmlmin: {
        dist: {
            options: {
                collapseWhitespace: true,
                collapseBooleanAttributes: true,
                removeCommentsFromCDATA: true,
                removeOptionalTags: true
            },
            files: [{
                expand: true,
                cwd: '<%= yeoman.dist %>',
                src: ['*.html', 'templates/**/*.html'],
                dest: '<%= yeoman.dist %>'
            }
            ]
        }
    },

    // Copies remaining files to places other tasks can use
    copy: {
        dist: {
            files: [
                {
                    expand: true,
                    dot: true,
                    cwd: '<%= yeoman.app %>',
                    dest: '<%= yeoman.dist %>',
                    src: [
                        '<%= yeoman.images %>/**/*.{png,jpg,jpeg,gif,webp,svg}',
                        '*.html',
                        'scripts/**/*.html',
                        'templates/**/*.html',
                        'fonts/*'
                    ]
                },
                {
                    expand: true,
                    dot: true,
                    cwd: '.temp/<%= yeoman.styles %>/',
                    dest: '<%= yeoman.dist %>/<%= yeoman.styles %>',
                    src: ['*.css']
                },
                {
                    expand: true,
                    cwd: '.temp/<%= yeoman.images %>',
                    dest: '<%= yeoman.dist %>/<%= yeoman.images %>',
                    src: ['generated/*']
                },
                {
                    expand: true,
                    flatten: true,
                    cwd: '<%= yeoman.app %>',
                    dest: '<%= yeoman.dist %>/fonts',
                    src: ['bower_components/ionicons/fonts/*.*']
                }

            ]
        },
        styles: {
            expand: true,
            cwd: '<%= yeoman.app %>/<%= yeoman.styles %>',
            dest: '.temp/<%= yeoman.styles %>/',
            src: '{,*/}*.css'
        },
        fonts: {
            expand: true,
            cwd: 'app/lib/ionic/release/fonts/',
            dest: '<%= yeoman.app %>/fonts/',
            src: '*'
        },
        vendor: {
            expand: true,
            cwd: '<%= yeoman.app %>/vendor',
            dest: '.temp/<%= yeoman.styles %>/',
            src: '{,*/}*.css'
        },
        app: {
            expand: true,
            cwd: '<%= yeoman.app %>',
            dest: '<%= yeoman.dist %>/',
            src: [
                '**/*',
                '!**/*.(scss,sass,css)'
            ]
        },
        tmp: {
            expand: true,
            cwd: '.temp',
            dest: '<%= yeoman.dist %>/',
            src: '**/*'
        }

    },

    concurrent: {
        ionic: {
            tasks: [],
            options: {
                logConcurrentOutput: true
            }
        },
        server: [
            'compass:server',
            'copy:styles',
            'copy:vendor',
            'copy:fonts'
        ],
        test: [
            'compass',
            'copy:styles',
            'copy:vendor',
            'copy:fonts'
        ],
        dist: [
            'compass:dist',
            'copy:styles',
            'copy:vendor',
            'copy:fonts'
        ]
    },
    filerev: {
        dist: {
            src: [
                '<%= yeoman.dist %>/scripts/{,*/}{,*/}{,*/}*.js',
                '<%= yeoman.dist %>/vendor/{,*/}{,*/}{,*/}*.js',
                '<%= yeoman.dist %>/styles/{,*/}*.css',
                '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
                '<%= yeoman.dist %>/styles/fonts/{,*/}*'
            ]
        }
    },

    // ngAnnotate tries to make the code safe for minification automatically by
    // using the Angular long form for dependency injection.
    ngAnnotate: {
        dist: {
            files: [{
                expand: true,
                cwd: '.temp/concat/<%= yeoman.scripts %>',
                src: '*.js',
                dest: '.temp/concat/<%= yeoman.scripts %>'
            }
            ]
        }
    }

});

// Register tasks for all Cordova commands
_.functions(cordovaCli).forEach(function(name){
    grunt.registerTask(name, function(){
        this.args.unshift(name.replace('cordova:', ''));
        // Handle URL's being split up by Grunt because of `:` characters
        if(_.contains(this.args, 'http')||_.contains(this.args, 'https')){
            this.args = this.args.slice(0, -2).concat(_.last(this.args, 2).join(':'));
        }
        var done = this.async();
        var exec = process.platform === 'win32' ? 'cordova.cmd' : 'cordova';
        var cmd = path.resolve('./node_modules/cordova/bin', exec);
        var flags = process.argv.splice(3);
        var child = spawn(cmd, this.args.concat(flags));
        child.stdout.on('data', function(data){
            grunt.log.writeln(data);
        });
        child.stderr.on('data', function(data){
            grunt.log.error(data);
        });
        child.on('close', function(code){
            code = code ? false : true;
            done(code);
        });
    });
});

// Since Apache Ripple serves assets directly out of their respective platform
// directories, we watch all registered files and then copy all un-built assets
// over to <%= yeoman.dist %>/. Last step is running cordova prepare so we can refresh the ripple
// browser tab to see the changes. Technically ripple runs `cordova prepare` on browser
// refreshes, but at this time you would need to re-run the emulator to see changes.
grunt.registerTask('ripple', ['wiredep', 'newer:copy:app', 'ripple-emulator']);
grunt.registerTask('ripple-emulator', function(){
    grunt.config.set('watch', {
        all: {
            files: _.flatten(_.pluck(grunt.config.get('watch'), 'files')),
            tasks: ['newer:copy:app', 'prepare']
        }
    });

    var cmd = path.resolve('./node_modules/ripple-emulator/bin', 'ripple');
    var child = spawn(cmd, ['emulate']);
    child.stdout.on('data', function(data){
        grunt.log.writeln(data);
    });
    child.stderr.on('data', function(data){
        grunt.log.error(data);
    });
    process.on('exit', function(code){
        child.kill('SIGINT');
        process.exit(code);
    });

    return grunt.task.run(['watch']);
});

// Dynamically configure `karma` target of `watch` task so that
// we don't have to run the karma test server as part of `grunt serve`
grunt.registerTask('watch:karma', function(){
    var karma = {
        files: ['<%= yeoman.app %>/<%= yeoman.scripts %>/**/*.js', '<%= yeoman.test %>/spec/**/*.js'],
        tasks: ['newer:jshint:test', 'karma:unit:run']
    };
    grunt.config.set('watch', karma);
    return grunt.task.run(['watch']);
});

// Wrap ionic-cli commands
grunt.registerTask('ionic', function(){
    var done = this.async();
    var script = path.resolve('./node_modules/ionic/bin/', 'ionic');
    var flags = process.argv.splice(3);
    var child = spawn(script, this.args.concat(flags), {stdio: 'inherit'});
    child.on('close', function(code){
        code = code ? false : true;
        done(code);
    });
});

grunt.registerTask('test', [
    'wiredep',
    'clean',
    'concurrent:test',
    'autoprefixer',
    'karma:unit:start',
    'watch:karma'
]);

grunt.registerTask('serve', function(target){
    if(target === 'compress'){
        return grunt.task.run(['compress', 'ionic:serve']);
    }

    grunt.loadNpmTasks('grunt-contrib-connect');

    grunt.registerTask('server', ['connect:server']);

    grunt.config('concurrent.ionic.tasks', ['ionic:serve', 'watch']);
    grunt.task.run(['wiredep', 'init', 'concurrent:ionic']);
});
grunt.registerTask('emulate', function(){
    grunt.config('concurrent.ionic.tasks', ['ionic:emulate:' + this.args.join(), 'watch']);
    return grunt.task.run(['init', 'concurrent:ionic']);
});
grunt.registerTask('run', function(){
    grunt.config('concurrent.ionic.tasks', ['ionic:run:' + this.args.join(), 'watch']);
    return grunt.task.run(['init', 'concurrent:ionic']);
});
grunt.registerTask('build', function(){
    return grunt.task.run(['init', 'ionic:build:' + this.args.join()]);
});

grunt.registerTask('init', [
    'clean',
    //'ngconstant:development',
    'wiredep',
    'concurrent:server',
    'autoprefixer',
    'newer:copy:app',
    'newer:copy:tmp'
]);

grunt.registerTask('build_web', [
    'clean:dist',
    'wiredep',
    'compass:dist',
    'useminPrepare',
    'concurrent:dist',
    'autoprefixer',
    'concat',
    'ngAnnotate',
    'copy:dist',
    'cdnify',
    'cssmin:generated',
    'cssmin:dist',
    'uglify',
    'filerev',
    'usemin',
    'htmlmin'
]);

grunt.registerTask('compress', [
    'clean',
    //'ngconstant:production',
    'wiredep',
    'useminPrepare',
    'concurrent:dist',
    'autoprefixer',
    'concat',
    'ngAnnotate',
    'copy:dist',
    'cssmin',
    'uglify',
    'usemin',
    'htmlmin'
]);
grunt.loadNpmTasks('grunt-contrib-connect');

grunt.registerTask('server', ['connect:server']);

grunt.registerTask('coverage',
    ['karma:continuous',
        'connect:coverage:keepalive'
    ]);

grunt.registerTask('default', [
    'wiredep',
    'newer:jshint',
    'karma:continuous',
    'compress'
]);
};

anjum121 avatar Sep 01 '15 13:09 anjum121

@anjum121

You will have to create your own web server because 'ionic server' creates a server that don't support modRewrite.

Here what I did:

1- Web Server for Production ( Nodejs + Express)

_server.js_

https://gist.github.com/lucasalmeida92/7f67f5183318d44f348b

2- Web Server for Development ( Nodejs + Gulp + BroswerSync + Connect History Api Fallback)

_gulpfile.js_

https://gist.github.com/lucasalmeida92/592185d6bfdc5626cf5a

3- Gulp task for Mobile App build (with gulp-replace package)

When you put <base href="/"> inside the head tag in your index.html file, the application will work fine with Html5Mode enabled in a web browser, but in your mobile application you gotta use <base href="."> instead.

To automate this, I created a gulp task with gulp-replace node package to do the job for me:

gulp.task('build:device', ['sass', 'compressJs', 'jade-templates'],function () {
  return gulp.src(paths.jade[1])
    .pipe(replace('base(href="/")', 'base(href=".")'))
    .pipe(jade())
    .pipe(gulp.dest('./www/'));
});

So before I run "ionic build android" i run "gulp build:device" .

4- Url paths Cautions

You will have to be careful with url paths.

  • href urls must start with "/". Ex.: <a href="/app/list">link</a>
  • src urls must start without "/". Ex.: <img src="img/banner.jpg">

lucasalmeida92 avatar Dec 29 '15 21:12 lucasalmeida92