Lazyloading unable to find module - nativescript

I'm new to nativescript - just installed it, so everything shoud be up-to-date. I've been trying to get lazy loading to work by following the guide on the nativescript website:
https://docs.nativescript.org/performance-optimizations/lazy-loading#implementing-lazy-loading-in-nativescript
I've followed the guide to the letter, only adding an empty service and providing a component with a template string. On running in the android emulator, I get the following exception:
JS: ERROR Error: Uncaught (in promise): Error: com.tns.NativeScriptException: Failed to find module: "feature/feature.module", relative to: app/tns_modules/
JS: com.tns.Module.resolvePathHelper(Module.java:146)
JS: com.tns.Module.resolvePath(Module.java:55)
JS: com.tns.Runtime.callJSMethodNative(Native Method)
JS: com.tns.Runtime.dispatchCallJSMethodNative(Runtime.java:1116)
JS: com.tns.Runtime.callJSMethodImpl(Runtime.java:996)
JS: com.tns.Runtime.callJSMethod(Runtime.java:983)
JS: com.tns.Runtime.callJSMethod(Runtime.java:967)
JS: com.tns.Runtime.callJSMethod(Runtime.java:959)
JS: com.tns.gen.java.lang.Object_view_31_32_TouchListenerImpl.onTouch(Object_view_31_32_TouchListenerImpl.java:18)
JS: android.view.View.dispatchTouchEvent(View.java:11721)
JS: android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2961)
JS: android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2650)
JS: android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2961)
JS: android.view.ViewGroup.dispatchTouchEvent(...
Executing before-prepare hook from C:\apps\stoiccompanion\hooks\before-prepare\nativescript-dev-typescript.js
Hook skipped because either bundling or livesync is in progress.
Preparing project...
Project successfully prepared (Android)
Successfully transferred app.routing.js.
Refreshing application...
Successfully synced application org.nativescript.StoicCompanion on device emulator-5554.
JS: Angular is running in the development mode. Call enableProdMode() to enable the production mode.
I've tried varous forms of path including ~/, / ./ etc, but still no joy.
I've also tried to use tns update in case something is out of date. Looking around online, I can see a few similar issues on their github, which has uncovered a couple of leads (ensure there are no uppercase filenames etc), but not have any luck.
If it matters, I'm using Windows 10.
Are there any common errors I should be looking out for, or are there any tools or logs I can investigate to try and get more details?
Here is my app.route
// app/app.routing.ts
import { NgModule } from "#angular/core";
import { NativeScriptRouterModule } from "nativescript-angular/router";
import { Routes } from "#angular/router";
import { ItemsComponent } from "./item/items.component";
import { ItemDetailComponent } from "./item/item-detail.component";
const routes: Routes = [
{ path: "", redirectTo: "/items", pathMatch: "full" },
{ path: "items", component: ItemsComponent },
{ path: "item/:id", component: ItemDetailComponent },
{ path: "feature", loadChildren: "~/feature/feature.module#FeatureModule" }, // lazy loaded module
];
#NgModule({
imports: [NativeScriptRouterModule.forRoot(routes)],
exports: [NativeScriptRouterModule]
})
export class AppRoutingModule { }
My app directory structure is:
src
app
feature
feature.component.ts
feature.module.ts
feature.service.ts
feature.routing.ts
app.module.ts
app.routing.ts
etc
Edit
Wrote a big edit, then found a typo in the filenames. Fixed that and it would appear to be working now. However, I still cant use loadChildren: in the app level router using a relative path ./app/feature/... works, but ~/feature/... doesnt.
Updating to ~/feature results in the following exception:
JS: Angular is running in the development mode. Call enableProdMode() to enable the production mode.
JS: ERROR Error: Uncaught (in promise): Error: com.tns.NativeScriptException: Failed to find module: "~/feature/feature.module", relative to: /app/
JS: com.tns.Module.resolvePathHelper(Module.java:146)
JS: com.tns.Module.resolvePath(Module.java:55)
JS: com.tns.Runtime.callJSMethodNative(Native Method)
JS: com.tns.Runtime.dispatchCallJSMethodNative(Runtime.java:1116)
JS: com.tns.Runtime.callJSMethodImpl(Runtime.java:996)
JS: com.tns.Runtime.callJSMethod(Runtime.java:983)
JS: com.tns.Runtime.callJSMethod(Runtime.java:967)
JS: com.tns.Runtime.callJSMethod(Runtime.java:959)
JS: com.tns.gen.java.lang.Object_view_31_32_TouchListenerImpl.onTouch(Object_view_31_32_TouchListenerImpl.java:18)
JS: android.view.View.dispatchTouchEvent(View.java:11721)
JS: android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2961)
JS: android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2650)
JS: android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2961)
JS: android.view.ViewGroup.dispatchTouchEvent(ViewGroup...
My tsconfig file has the following entry:
"paths": {
"~/*": [
"src/*"
],
"*": [
"./node_modules/tns-core-modules/*",
"./node_modules/*"
]
}
I found the following link, which seems to imply I should update the ~/ alias to point to /app, which I tried, but still had the same problem. I also tried to update with ./src/app
https://github.com/NativeScript/nativescript-dev-webpack/issues/372

I was facing the same problem. I dabbled around, and it worked by adding ./app/ string inside loadChildren, like so
Inside app-routing.module.ts
const routes: Routes = [
{
path: "",
redirectTo: "/home",
pathMatch: "full"
},
{
path: "home",
component: HomeComponent
},
{
path: "collection",
loadChildren: "./app/collection/collection.module#CollectionModule"
},
{
path: "product",
loadChildren: "./app/product/product.module#ProductModule"
}
];
and its working if routed to a lazy loaded module from a component, such as from home.component.html
<Button text="home works!" class="btn btn-primary" [nsRouterLink]="['/collection']"></Button>
and it's working if you make a lazy loaded module as a first route.
But all of this would also depend on the project structure, normally in angular you have a structure, where you put the modules inside the app folder, like so

Related

NativeScript vue, vuex and urlhandler

Edit
I'm using https://github.com/hypery2k/nativescript-urlhandler to open a deep link within my app - using NativeScript vue, and vuex. It seems that in order to get at the methods needed to do routing [$navigateTo etc] this plugin needs to be set up slightly differently from the examples given in docs.
import Vue from "nativescript-vue";
import Vuex from "vuex";
Vue.use(Vuex);
import { handleOpenURL } from 'nativescript-urlhandler';
new Vue({
mounted() {
handleOpenURL( (appURL) => {
console.log(appURL)
// Settings is the variable that equals the component - in this case settings.
this.$navigateTo(Settings);
});
},
render: h => h("frame", [h(Home)]),
store: ccStore
}).$start();
handleOpenURL needs to be called within Mounted - then you can parse out the appURL and reference the page (component) that you wish to navigate to. I have been advised against calling handleOpenURL from within router - but I'm not sure why, and it works without error - and I have access to the methods for routing... so if anyone knows if this is a bad idea - please let me know :) Thanks!
All the stuff below that I wrote before has probably confused things - I'm referencing components within my vuex store to make them easily available from the router.
This is based on a solution by https://github.com/Spacarar - it can be found here: https://github.com/geodav-tech/vue-nativescript-router-example. It's a great solution because you don't have to include every single component within each component to use in navigation - it gives an almost vue router like experience.
I'm using https://github.com/hypery2k/nativescript-urlhandler to open a deep link within my app - however, I'm having problems opening the link.
In my app.js file, I have the following:
import Vue from "nativescript-vue";
import Vuex from "vuex";
Vue.use(Vuex);
....
import { handleOpenURL } from 'nativescript-urlhandler';
import ccStore from './store/store';
handleOpenURL(function(appURL) {
// I have hardwired 'Settings' in for testing purposes - but this would be the appURL
ccStore.dispatch('openAppURL', 'Settings');
});
....
new Vue({
render: h => h("frame", [h(Home)]),
store: ccStore
}).$start();
I'm storing the route state within vuex, and have various methods which work (clicking on a link loads the component). However, handleOpenURL exists outside of vue... so I've had to access vuex directly from within the handleOpenURL method. I've created an action specifically for this case - openAppURL.. it does exactly the same thing as my other methods (although I've consolidated it).
When clicking on an app link, I am NOT taken to the page within the app. I have put a console log within openAppURL and can see it is being called, and the correct route object is returned... it just doesn't open the page. The SetTimeOut is used because nextTick isn't available from within vuex.
I am at a loss on how to get the page to appear...
const ccStore = new Vuex.Store({
state: {
user: {
authToken: null,
refreshToken: null,
},
routes: [
{
name: "Home",
component: Home
},
{
name: "Log In",
component: Login
},
...
],
currentRoute: {
//INITIALIZE THIS WITH YOUR HOME PAGE
name: "Home",
component: Home //COMPONENT
},
history: [],
},
mutations: {
navigateTo(state, newRoute, options) {
state.history.push({
route: newRoute,
options
});
},
},
actions: {
openAppURL({state, commit}, routeName ) {
const URL = state.routes[state.routes.map( (route) => {
return route.name;
}).indexOf(routeName)];
return setTimeout(() => {
commit('navigateTo', URL, { animated: false, clearHistory: true });
}, 10000);
},
....
}
etc....
I have been advised to post my findings as the answer and mark it as correct. In order to use nativescript-urlhandler with vue, you must initialise the handler from within vue's mounted life cycle hook. Please see above for greater detail.
import Vue from "nativescript-vue";
import Vuex from "vuex";
Vue.use(Vuex);
import Settings from "~/components/Settings";
import { handleOpenURL } from 'nativescript-urlhandler';
new Vue({
mounted() {
handleOpenURL( (appURL) => {
console.log(appURL) // here you can get your appURL path etc and map it to a component... eg path === 'Settings. In this example I've just hardwired it in.
this.$navigateTo(Settings);
});
},
render: h => h("frame", [h(Home)]),
store: ccStore
}).$start();

Angular 2 Unit Test. Test Route with login screen and hidden menu component

I have to realise Angular 2 unit Test on routes and I fail with errors.
My application start with that content within app.component.html
<app-top-menu></app-top-menu>
<router-outlet></router-outlet>
I have those routes:
const appRoutes: Routes = [
{path: 'login', component: LoginComponent},
{path: 'home', component: HomeComponent, canActivate: [AuthGuard]},
{path: 'Second', component: SecondComponent, canActivate: [AuthGuard]},
{path: 'Third', component: ThirdComponent, canActivate: [AuthGuard]},
{path: 'Fourth', component: FourthComponent, canActivate: [AuthGuard]},
{path: '', redirectTo: '/login', pathMatch: 'full'},
{path: '**', component: PageNotFoundComponent}
];
When you acccess the application you're first redirected toward a login page for authentification.
All other page can only be accessed if logged.
Within app.component.html we first have app-top-menu before router-outlet.
The component app-top-menu is hidden and appear only once the user is logged.
This tricked has been made to hide the app-top-menu component from login page.
When we log in login component we do that action to display the component app-top-menu
this.globalEventsManager.showNavBar(true);
Everyting works fine when I run ng serve - I use angular cli.
The UI is working very well.
But when I run ng test I get a first error in app.component
AppComponent should create the app
Failed: Template parse errors:
'router-outlet' is not a known element:
1. If 'router-outlet' is an Angular component, then verify that it is part of this module.
2. If 'router-outlet' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '#NgModule.schemas' of this component to suppress this message. ("<app-top-menu></app-top-menu>
[ERROR ->]<router-outlet></router-outlet>"): AppComponent#1:0
then a second within SecondComponent about routerLink.
Failed: Uncaught (in promise): Error: Template parse errors:
Can't bind to 'routerLink' since it isn't a known property of 'a'. ("
<h1>Home</h1>
<p>You're logged in with JWT!!</p>
<p><a [ERROR ->][routerLink]="['/login']">Logout</a></p>
</div>"): SecondComponent#3:10
Error: Template parse errors:
Can't bind to 'routerLink' since it isn't a known property of 'a'. ("
Here is the content of my app.component.spec.ts
describe('AppComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent,
TopMenuComponent
]
});
TestBed.compileComponents();
});
it('should create the app', async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
}));
How can I mock the router and test routes.
Also the app-top-menu is involved as it appears/disappear according to login.
Thanks for your help.

Require a jQuery-Plugin with Webpack

I want to use Webpack in order to create one single scripts.js file out of all needed Javascript files.
Within my main.js I require three modules:
require('jquery');
require('readmore');
require('foundation');
My webpack.config.js is this:
var path = require('path');
module.exports = {
entry: ["./js/main.js"],
output: {
path: path.resolve(__dirname, 'build'),
filename: "scripts.js"
},
resolve: {
modulesDirectories: ["bower_components", "node_modules"],
alias: {
jquery: '../bower_components/jquery/dist/jquery.js',
readmore: '../node_modules/readmore-js/readmore.js',
foundation: '../bower_components/foundation-sites/dist/js/foundation.js'
}
}
}
My problem: as readmore-js is a jQuery-Plugin it requires jQuery by itself.
I got this error after running Webpack:
ERROR in ./~/readmore-js/readmore.js
Module not found: Error: Can't resolve 'jquery' in '/Users/myName/www/myProject/node_modules/readmore-js'
# ./~/readmore-js/readmore.js 17:4-31
# ./js/main.js
# multi main
From my understanding the problem is that readmore also wants to load the module jQuery within the directory "nodes_modules". My first approach was to resolve this problem by adding moduleDirectories to the config-file, but it does still not work.
And even in this case, the plugin shouldn't load jQuery again.
Do you have any idea how I can load jQuery globally and then "tell" all modules which require jQuery by themself "look, it's there!"
As it may helps, the following is copied out of the plugin's readmore.js:
(function(factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// CommonJS
module.exports = factory(require('jquery'));
} else {
// Browser globals
factory(jQuery);
}
}
You can use webpack.ProvidePlugin for this:
Remove require jquery from main.js:
require('readmore');
require('foundation');
Configure webpack.ProvidePlugin inside webpack.config.js:
var path = require('path');
module.exports = {
entry: ["./js/main.js"],
output: {
path: path.resolve(__dirname, 'build'),
filename: "scripts.js"
},
resolve: {
modulesDirectories: ["bower_components", "node_modules"],
alias: {
readmore: '../node_modules/readmore-js/readmore.js',
foundation: '../bower_components/foundation-sites/dist/js/foundation.js'
}
},
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery'
}),
]
}

$urlRouterProvider.otherwise is not a function

I have this app.js file
angular.module('myApp', [
'myApp.version',
'ui.router'
]).
config(['$locationProvider','$urlRouterProvider','$stateProvider', function($locationProvider,$stateProvider, $urlRouterProvider, $httpProvider) {
$urlRouterProvider.otherwise('/view1');
$stateProvider
.state('view1', {
url: '/view1',
templateUrl: 'partials/view1.html'
//controller: 'view1.MainController'
})
.state('view2', {
url: '/view2',
templateUrl: 'partials/view2.html'
})
.state('view3', {
url: '/view3',
templateUrl: 'partials/view3.html'
})
.state('view4', {
url: '/view4',
templateUrl: 'partials/view4.html'
});
}]);
I have included "https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.3.1/angular-ui-router.min.js" CDN in my index.html file and it is getting loaded fine.
But even when it is loaded the console is giving an error as
Failed to instantiate module myApp due to:
TypeError: $urlRouterProvider.otherwise is not a function
I tried removing this line and then test the code, but then it gives
Failed to instantiate module myApp due to:
TypeError: $stateProvider.state is not a function
I have gone through the code multiple times but I am unable to identify the mistake/error.
(angular version is 1.5.8 and angular-ui-router version is 0.3.1 ,if that helps)
Please Help!
You inverted the parameters: urlRouterProvider and stateProvider.
wrong order:
config(['$locationProvider', '$stateProvider', '$urlRouterProvider', function($locationProvider, $urlRouterProvider,$stateProvider, $httpProvider) {
right order:
config(['$locationProvider','$urlRouterProvider','$stateProvider', function($locationProvider, $urlRouterProvider,$stateProvider, $httpProvider) {
You used that technique to avoid problems with minification, but the order of the "stringed-names" must be the same to the order of the function parameters.
I bet you know that and just mistyped.

How to get webpack hot module replacement (react-hot-loader) working when proxying own socket.io server

I have a node express app that is using socket.io.
This app is proxied through webpack's dev server as I want to use hot module replacement in the client side react app (which communicates back to the socket code on the node app.)
If I add my socket.io listener however it breaks the hot module replacement stuff. I guess because my listener is receiving the messages instead of the hmr listener?
The problem is that when I save a file that would be hot loaded, instead i get the following in chrome dev tools and NO hot load:
[WDS] App updated. Recompiling...
client?eeaa:52 [WDS] Proxy error.
client?eeaa:54 cannot proxy to http://127.0.0.1:1338 (read ECONNRESET)
client?eeaa:90 [WDS] App hot update...
socket.io.js:1456 GET http://localhost:1339/socket.io/?EIO=3&transport=polling&t=LA7JXr9&sid=r1NFzh1HOD5GcbN0AAAA 502 (Bad Gateway)
client?eeaa:25 [WDS] App updated. Recompiling...
client?eeaa:90 [WDS] App hot update...
socket.io.js:1456 POST http://localhost:1339/socket.io/?EIO=3&transport=polling&t=LA7Jckl&sid=r1NFzh1HOD5GcbN0AAAA 400 (Bad Request)
(The body of the 400 (Bad Request) is:
{"code":1,"message":"Session ID unknown"}
Anyone know how to set this up properly?
MY webpack.config.js is currently:
var path = require('path');
var webpack = require('webpack');
module.exports = {
entry: [
'webpack-dev-server/client?http://127.0.0.1:1339/',
'webpack/hot/only-dev-server',
'./src/client/main.js'
],
output: {
path: path.join(__dirname, 'dist'),
filename: 'client-bundle.js'
},
resolve: {
modulesDirectories: ['node_modules'],
extensions: ['', '.js']
},
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loaders: ['react-hot', 'babel']
}, {
test: /\.css$/,
loader: 'style!css'
}
]
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin()
],
devtool: 'inline-source-map',
devServer: {
hot: true,
proxy: {
'*': 'http://127.0.0.1:' + (process.env.PORT || 1338)
},
host: '127.0.0.1'
}
};

Resources