Ui-router child state not loading inside downgraded Angular component - angular-ui-router

I have a hybrid/lazy-loaded Angular/AngularJS application which uses both the new Angular router and ui-router version 0.4.2 with parallel outlets/views. To accomplish this I have followed Victor Savkin's Lazy Loaded AngularJS guide In general, this is working well and I can switch between Angular routes and AngularJS routes. However, I am running into an issue where ui-router child views are not shown when the page first opens.
This issue only affects states if trying to load the child state on boot. Navigating between routes does not encounter the same issue. I have also determined that it only occurs in routes when the parent state template is wrapped by a downgraded Ng2 component.
State and Template Definitions
#Component({ template: '<ng-content></ng-content>'})
export class TestDowngradedComponent { }
angular.module('routerPatchModule', ['ui.router'])
.directive('downgradedComp', downgradeComponent({ component: TestDowngradedComponent }))
.config(['$stateProvider', ($stateProvider) => {
$stateProvider.state({
name: 'parent',
url: '/parent',
template: '<downgraded-comp><ui-view></ui-view></downgraded-comp>',
})
.state({
name: 'parent.test',
url: '/test',
template: 'The child route doesn't appear on load',
})
}]);
/**
* Ng2 Module which upgrades the legacy Ng1 module
*/
#NgModule({
declarations: [
EmptyTemplateComponent,
TestDowngradedComponent,
],
entryComponents: [
TestDowngradedComponent,
],
imports: [
SharedModule,
UpgradeModule,
RouterModule.forChild([
{path: '**', component: EmptyTemplateComponent},
]),
],
})
export class LegacyAppModule {
constructor(upgrade: UpgradeModule) {
upgrade.bootstrap(document.body, [ng1AppModule, 'routerPatchModule'], { strictDi: true });
setUpLocationSync(upgrade);
}
}
Tracking the state change events, I was able to determine that the child view is temporarily loaded but then removed.
State Event Log
$viewContent Loading undefined {"view":"#"}
$viewContent Loaded undefined {"view":"#"}
$stateChange Start state1.child
$stateChange Success state1.child
$viewContent Loading undefined {"view":"#"}
$viewContent Loading state1 {"view":"#state1"}
$viewContent Loaded state1.child {"view":"#state1"} // child view temporarily loaded
$viewContent Loaded state1 {"view":"#"} // child view gone!
// User clicks link to state2
$stateChange Start state2
$stateChange Success state2
$viewContent Loading undefined {"view":"#"}
$viewContent Loading state2 {"view":"#state2"}
$viewContent Loaded state2 {"view":"#state2"}
$viewContent Loaded state2 {"view":"#"}
$stateChange Start state2.child
$stateChange Success state2.child
$viewContent Loading state2 {"view":"#state2"}
$viewContent Loaded state2.child {"view":"#state2"} // Child loaded successfully
// User clicks link to state1
$stateChange Start state1
$stateChange Success state1
$viewContent Loading undefined {"view":"#"}
$viewContent Loading state1 {"view":"#state1"}
$viewContent Loaded state1 {"view":"#state1"}
$viewContent Loaded state1 {"view":"#"}
$stateChange Start state1.child
$stateChange Success state1.child
$viewContent Loading state1 {"view":"#state1"}
$viewContent Loaded state1.child {"view":"#state1"} // Child loaded after parent
What about using a component with <ng-content> causes the ui-router to not render correctly and what can I do to correct the issue?

When using a ui-view directive inside of a downgraded component, wrap the ui-view directive in a div.
$stateProvider.state({
name: 'parent',
url: '/parent',
template: '<downgraded-comp><div><ui-view></ui-view></div></downgraded-comp>',
})
I stumbled upon a solution by accident and am not sure exactly why it worked. I wonder if it is the result of dom elements being replaced by the #angular/upgrade library.

Related

Error when loading laravel login page? Uncaught (in promise) TypeError: Cannot read property 'call' of undefined

I haven't touched the webpack since the beginning of my project at all, suddenly I get this error.
I'm trying to load the login page and get this error and nothing loads
Uncaught (in promise) TypeError: Cannot read property 'call' of undefined
at __webpack_require__ (app.js:64)
at app.js:73038
No idea what this is about, this is my webpack.mis.js
const mix = require('laravel-mix');
const path = require('path');
mix.js('resources/js/app.js', 'public/js')
.sass('resources/sass/app.scss', 'public/css')
.webpackConfig({
output: { chunkFilename: 'js/[name].js?id=[chunkhash]' },
resolve: {
alias: {
vue$: 'vue/dist/vue.runtime.esm.js',
'#': path.resolve('resources/js'),
},
},
})
.babelConfig({
plugins: ['#babel/plugin-syntax-dynamic-import'],
})
.version();
I haven't changed anything in my webpack, and it was working before as in showing me the page, all I did was move the User model from app to app/Models and change all the mentions of app/User to app/Models/User.

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.

Testing Twitter Flight Component with Jasmine

Having issues running Jasmine Test with Twitter Flight. I'm using the jasmine-flight Plugin.
Error(s)
Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
TypeError: this.Component is not a constructor
at Object.setupComponent (/frontend/bower_components/jasmine-flight/lib/jasmine-flight.js:44:23)
at Object.<anonymous> (/frontend/test/spec/component_ui/list-spec.js:5:10)
TypeError: Cannot read property 'trigger' of null
at Object.<anonymous> (/frontend/test/spec/component_ui/list-spec.js:19:19)
Test Component
describeComponent('javascript/component_ui/list-ui', function() {
'use strict';
beforeEach(function() {
jasmine.getFixtures().fixturesPath = 'base/test/fixtures';
this.setupComponent(
readFixtures('list.html'),
{
listRightMenu: '.list-right',
listSortSelector: '.list-sort',
}
);
});
it('Show menu when button is clicked', function() {
console.log(this);
this.component.trigger(this.component.attr.listSortSelector, 'click');
expect(this.component.attr.listRightMenu).toBeVisible();
});
});

Show Modal on AngularJS $httpProvider: responseError

We need to hookup a Modal with a generic message on our failed Ajax calls in AngularJS.
i was able to intercept the error on the app.config part, but from there i am unable to import the $modal or the $scope to show the modal. What i got working so far is creating a function on my global scope and add the opening of the modal in my app.run:
var showGeneralException = function () { };
var app= angular.module('myApp', ['ui.bootstrap'])
.run(function ($rootScope, $modal) {
showGeneralException = function () {
var exceptionModel = $modal.open({
templateUrl: 'generalException',
controller: 'exceptionModals',
});
};
});
app.config(function ($httpProvider) {
$httpProvider.interceptors.push(function ($q) {
return {
'responseError': function (rejection) {
showGeneralException();
}
};
});
});
When i perform it this way a modal opens but i get a not found exception in my modal and following errors in my debug console:
TypeError: Unable to get value of the property 'data': object is null or undefined
LOG: WARNING: Tried to load angular more than once.
LOG: WARNING: Tried to load angular more than once.
LOG: WARNING: Tried to load angular more than once.

ui-router fires transition to root state as soon as controller loaded

has anyone seen this sort of behaviour before ?
I have a button that is defined like this
<button ui-sref="designer({model: 'foobar' })">
I have a state defined as this
{
name: 'designer',
state: {
url: 'designer/:model/:form',
templateUrl: '/partials/designer.html',
controller: 'DesignerController',
controllerAs: 'designer'
}
}
and a controller like this
(function() {
angular.module('myApp')
.controller('DesignerController', DesignerController)
function DesignerController() {
console.log("-->loaded designer");
}
})();
when I click on the button, I get this in the console (I've added some debugging)
$stateChangeSuccess to designer- fired once the state transition is complete.
-->loaded designer
$viewContentLoaded - fired after dom rendered Object {name: "$viewContentLoaded", targetScope: ChildScope, defaultPrevented: false, currentScope: Object}
$stateChangeStart to undefined- fired when the transition begins. toState,toParams :
Object {url: "/", templateUrl: "/partials/main.html", name: "root", status: "exited", onExit: undefined} Object {}
$stateChangeSuccess to root- fired once the state transition is complete.
as you can see - as soon as the designer controller is loaded, a transition to the root state is fired - what can cause that ?

Resources