Dart Angular 5 router child redirectTo needs parent parameter - angular-dart

We need to redirect a child route to another child route and maintain a parent route parameter. For example redirect the route /u/2/object/123 to /u/2/list. Routing (for this simplified example) is handled by parent and child RouteDefinition levels. The parent definition would be
RouteDefinition(path: 'u/:index', component: ...
The child would be
RouteDefinition(path: 'list', component: ...
RouteDefinition(path: 'object/:id', component: ...
RouteDefinition.redirect(path: '.*', redirectTo: 'u/:index/list')
Running this returns error FormatException: :index so parameters in redirectTo URLs don't seem to be currently supported. Relative routes are also not supported.
We are using Dart Angular 5 so RouteDefinitions must be defined and given to the router-outlet before any of the router events fire. This seems to preclude use of procedural redirection at the child level. Are there any other solutions other than overriding the top level router?

I created a component that only redirects in its CanActivate handler. The child RouteDefinition uses a component instead of a .redirect:
new RouteDefinition(path: '.*', component: RedirectTo.RedirectToNgFactory),
I have RoutePath definitions:
final user = new RoutePath(path: 'u/:index');
final list = new RoutePath(parent: user, path: 'list');
The component's CanActivate handler is given the route path and parameters. It always does a router.navigate:
#Component(
selector: 'redirect-to',
template: '',
)
class RedirectTo implements CanActivate {
final Router router;
RedirectTo(this.router);
Future<bool> canActivate(RouterState current, RouterState next) async {
router.navigate( list.toUrl(parameters: next.parameters) );
return false;
}
}

Related

Angular 7 routing not working when clicking on the leaflet marker

I am working with angular 7 and leaflet js for the map representation.
I want to navigate to another page when click on the marker. But routing is not working properly.
eg:
L.marker.on('click', function(){
this.router.navigateByUrl('/dashboard');
});
when click on the marker, the url changed to '/dashboard' but the map still showing in the page.
when click on the html element, navigation working fine.
Could any one please help me on this.
Thanks in advance
You need to define the two routes and then listen to the marker on click event to be able to navigate.
For instance have these two routes map and dashboard on app.module.ts and land on map view when initializing the app:
const appRoutes: Routes = [
{ path: "map", component: MapComponent },
{ path: "dashboard", component: DashboardComponent },
{ path: "", redirectTo: "/map", pathMatch: "full" }
];
#NgModule({
declarations: [AppComponent, MapComponent, DashboardComponent],
imports: [BrowserModule, RouterModule.forRoot(appRoutes)],
...
})
and then add <router-outlet></router-outlet> on app.html to be able to navigate to different pages
Then you might have a map component where the map will be held. Add the marker on the map, listen to the click event & navigate to dashboard page:
L.marker([51.505, -0.09], this.markerIcon)
.addTo(this.map)
.on("click", e => {
this.router.navigateByUrl("/dashboard");
});
I also added a button to navigate back to map component from the dashboard page
Demo
It is obviously too late to answer the original question, but if anybody gets here with the same issue, here is the solution.
The origin of the problem is that when we are trying to execute Angular navigation directly in the Leaflet event callback. But Leaflet map with its markers is outside Angular zone (= execution context):
marker.on('click', () => { // we are in Leaflet
this.router.navigate('/dashboard'); // but this is Angular
});
So the solution is to run the navigation code from Angular zone:
marker.on('click', () => {
this._ngZone.run(() => { // here we are back in Angular
this.router.navigate('/dashboard');
});
});
Don't forget to include private _ngZone: NgZone, in component constructor.
For more detailed explanation check this one. The message in the title is the one you see as warning in console when trying to execute the first code section above.

Angular Reactive forms - share state with children inside loop

How can a form inside a child component extend an existing form passed in by a parent component?
Desired effect: if one of the child forms is invalid, the parent form should become invalid as well.
I am using reactive forms. The number of child components can vary.
export class AddressFormComponent implements OnInit {
#Input() addressesForm; // This is the parent form
#Input() addressData;
addressForm;
constructor(private formBuilder: FormBuilder) {}
ngOnInit() {
this.addressForm = this.formBuilder.group({
...
});
// Trying to add this to the parent form will break the application (Maximum call stack size exceeded)
this.addressesForm.push(this.addressesForm);
}
I have made a simplified plunker: https://plnkr.co/edit/GymI5CqSACFEvhhz55l1?p=preview.

React/Redux and Websockets for Timer actions

Here is my use case:
I have two different apps, react client app and express/node backend server app having REST APIs. I want the react client app to refresh the component states, every time the Server sends an event on the socket which has change of the data on the server side.
I have seen examples of websocket doing this (http://www.thegreatcodeadventure.com/real-time-react-with-socket-io-building-a-pair-programming-app/) but in this case the client and the server components are in the same app. How to do this when you different apps for client and the server components.
Should I use Timer (https://github.com/reactjs/react-timer-mixin) to make a call from the client to the server rest endpoint and refresh the components on the client if there is any change in data. Or does redux middleware provide those capabilities..
thanks, Rajesh
I think what you are looking for is something like react-redux. This allows you to connect the component to depend on a piece of the state tree and will be updated whenever the state changes (as long as you are applying new references). See below:
UserListContainer.jsx
import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as UserActions from '../actions/userActions';
import UserList from '../components/UserList';
class UserListContainer {
// Subscribe to changes when the component mounts
componentDidMount() {
// This function
this.props.UserActions.subscribe();
}
render() {
return <UserList {...props} />
}
}
// Add users to props (this.props.users)
const mapStateToProps = (state) => ({
users: state.users,
});
// Add actions to props
const mapDispatchToProps = () => ({
UserActions
});
// Connect the component so that it has access to the store
// and dispatch functions (Higher order component)
export default connect(mapStateToProps)(UserListContainer);
UserList.jsx
import React from 'react';
export default ({ users }) => (
<ul>
{
users.map((user) => (
<li key={user.id}>{user.fullname}</li>
));
}
</ul>
);
UserActions.js
const socket = new WebSocket("ws://www.example.com/socketserver");
// An action creator that is returns a function to a dispatch is a thunk
// See: redux-thunk
export const subscribe = () => (dispatch) => {
socket.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'user add') {
// Dispatch ADD_USER to be caught in the reducer
dispatch({
type: 'ADD_USER',
payload: {
data.user
}
});
}
// Other types to change state...
};
};
Explanation
Essentially what is happening is that when the container component mounts it will dispatch a subscribe action which will then listed for messages from the socket. When it receives a message it will dispatch another action base off of its type with the corresponding data which will be caught in the reducer and added to state. *Note: Do not mutate the state or the component will not reflect the changes when it is connected.
Then we connect the container component using react-redux which applies state and actions to props. So now any time the users state changes it will send it to the container component and down to the UserList component for rendering.
This is a naive approach but I believe it illustrates the solution and gets you on the right track!
Good luck and hope this helped!

asp.net mvc6 / angular2 routing

I have asp.net mvc6 project. On client side i want to use angular2 to create single page application. It means i have two places where i can define routing.
As far it's SPA, routing has to be on angular2 side, but how to deal in case when user is refreshing page? MVC returns 404.
One way would be to set Startup route in mvc6:
routes.MapRoute(
name: "spa-fallback",
template: "{*url}",
defaults: new { controller = "OAuth", action = "Index" });
});
And then controller gives Request.Path to angular2.
However this {*url} does not recognize *.map and other static files.
Also there may be scenarious where i would like to use static page like Error.html etc.
Is there any better way to deal with routing on both (server/client) side?
I was facing the same issues and decided to use views instead of static files.
I have setup MVC with one Home controller that has 2 actions: Index and Login.
The app boostraps in the index.cshtml and the login.cshtml is a simple login page.
Routing in my startup.cs file is configured this way:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "login",
template: "login",
defaults: new { controller = "Home", action = "Login" });
routes.MapRoute(
name: "spa-fallback",
template: "{*url}",
defaults: new { controller = "Home", action = "Index" });
});
In my AppComponent I have (using Angular RC1):
#Routes([
{ path: '/Dashboard', component: Dashboard}
])
refreshing the page in http://localhost/Dashboard keeps me on the dashboard view, whereas http://localhost/Login redirects me to the login page, out of the angular app).
Hope this helps

Routing in AngularJS with variable in the beginning of the URL

I'm experiencing some behavior that I can't explain, but I'm sure somebody else can. I'm setting up my app module like so:
var appmodule = angular.module('appmodule', ['ui']).config(
['$routeProvider', function($routeProvider) {
$routeProvider.
when('/dashboard', {
templateUrl: 'partials/dashboard.html',
controller: dashCtr,
layer: 'main'
}).
when('/:campaignId/edit', {
templateUrl: 'partials/edit.html',
controller: campaignCtr,
layer: 'layer1'
}).
when('/:campaignId', {
templateUrl: 'partials/campaign_info.html',
controller: campaignCtr,
layer: 'layer1'
}).
when('/inbox', {
templateUrl: 'partials/inbox.html',
controller: inboxCtr,
layer: 'main'
}).
otherwise({redirectTo: '/dashboard'});
}]);
When I navigate to app.com/:campaignId or app.com/:campaignId/edit, all works as expected, but if I try app.com/inbox (or anything else), I get redirected back to /dashboard. I've read in a few other places a URL can't begin with a variable, but I'm new to this concept (usually just to design side of the front-end) and don't understand why.
Thanks.
Well it is simply because inbox route is below ''/:campaignId' route. Angular checks rules in the order they were defined, and ''/:campaignId' rule is matching every single url, so there is no chance to get to 'inbox' route.
Just define '/:campaign' routes after all other routes and everything should be working.