Keep browser URL in sync with application status

Issue

This question is related to this question
Routing: get notified about parameter updates without reloading the view

I have a view with a grid-view (generated using ng-repeat).
The routing parameter recId specifies which row is made current initially.

The view has navigation buttons (forward backward) to select the current record. A click in a row also makes this row the current row.

This kind of (view internal) navigation doesn’t make use of AngularDarts routing functionality after the initial load of the view.
The back/forward buttons don’t reflect navigation inside the view.
As soon as I change the current record inside the view, the browser URL is out of sync.

If I change the browser URL each time the user uses a navigation action, the view get’s reloaded.
This makes development more complicated and leads to ugly reloads of the view (which is WIP – see linked question).

What is the best/correct way to keep application state and browser URL (history) in sync.

Update

How do I configure the router so that a click on one of this generated links updates the URL in the browser but doesn’t reload the view. (It should be possible to create a bookmark for the currently selected record.)

The code is a bit outdated but I didn’t work on this project since I asked this question.

The router configuration

library my_prototype.routing;

import 'package:angular/angular.dart';
import '../services/injectable.dart';

@InjectableService()
class AppRouteInitializer {


  call(Router router, ViewFactory views) {
    router.root
    ..addRoute(
        name: 'table',
        path: '/:entityId/table/:recId',
        enter: views('view/dynamic_table.html')
        )
    ..addRoute(
        name: 'default_view',
        defaultRoute: true,
        enter: (_) =>
            router.go('form',
                {
                  'entityId': '',
                  'rec': '-1'
                },
                replace: true));
  }
}

index.html contains beside Dart/AngularDart boilerplate only

<ng-view></ng-view>

The view component contains this constructor

@NgComponent(
  selector: 'dynamic-table',
  publishAs: 'ctrl',
  template: r'''  
    <div ng-repeat="rec in ctrl.records">
      <a href="/{{entityId}}/table/{{rec.id}}">{{rec.name}}</a>
    <div>''')
class DynamicTableComponent {
  HttpService _httpService;
  RouteProvider _routeProvider;
  String entityId;
  List<SomeRecord> records = [];

  DynamicTableComponent(this._routeProvider, this._httpService) {
    _log.fine('DynamicFormComponent');

    entityId = _routeProvider.parameters['entityId'];

    ... render table
  }
}

I got a notification today about a pull request that might be about this topic

Solution

After some research and testing, the short answer is : you need to use history API

In the history API, you can find the function pushState, that is usefull to change the URL location without reloading the page.

Information

In your case, i have create a little project to try to reproduce your issue : Carousel

In your case, the issue is to sync the Router of angular dart with the url of the browser, but :

1. I have found no way to do this in Angular

In angular, it’s possible to get the Router object but :

  • .route(url) will reload the page
  • .url(url) will reload the page
  • .go(..) will reload the page

so it’s not possible (with my research, maybe i have miss something) with angular

2. it’s not possible with location

Location will also reload the page

Solution

So the only things i have found to do what you want is the history API, like this :

import 'dart:html'

void main() {
   window.history.pushState(null, "title", "your/new/url");
}

but a little issue with this it’s that no event are fired when you change the state, so to fix this behavior, i have create a little wrapper of the class to send an event when the pushState() function is called

class CustomHistory {

  // Some code

  void pushState(Object data, String title, [String url]) {
    window.history.pushState(data, title, url);
    this._onPushStateEvent.add(new StateEvent(data, title, url)); // i have create a StateEvent object myself
  }
}

this allow me to do in my class a listener to this event :

 history.onPushStateEvent.listen((e) {
      var l = e.url.split("/");
      this.section = l[2];
      this.id = int.parse(l[3]);
      this.text = getText();
    });

with this, you can in your constructor set correctly your state, attribute etc… you know that if you constructor is called it’s a page loading or reloading, and after that manager your synchronization with the history and some function that will listen some event.

Link

Note :

I’m not sure if i answer correctly to your question, if not warn me and i will delete this answer

Answered By – Vink

Answer Checked By – Jay B. (FlutterFixes Admin)

Leave a Reply

Your email address will not be published.