Skip to content
This repository was archived by the owner on Feb 22, 2018. It is now read-only.

ng-view contents reevaluated for the same route #837

Closed
michalpie opened this issue Apr 2, 2014 · 6 comments
Closed

ng-view contents reevaluated for the same route #837

michalpie opened this issue Apr 2, 2014 · 6 comments
Milestone

Comments

@michalpie
Copy link

I have the following problem: the application has a static top panel which contains a horizontal main menu. The central area contains ng-view which changes accordingly to the selection of the main menu item. In the ng-view, for some of the main menu items, we want to display another navigation panel with a vertical sub-menu and a detail panel. The detail panel is another ng-view and changes accordingly to the selection of the sub-menu item.

E.g. for recipe book application if want to have some main menu items like: home, recipes, gallery, contact, and for recipes I have e.g. list of recipes

Lines represent ng-views:

     MAIN MENU
---------------------------
|          --------------- | 
| SUB-MENU | DETAIL PANEL| |
|          |             | |
|          |_____________| |
|__________________________|



Routes:
    /home (template home.html)
    /recipes (template recipes.html with a sub-menu and <ng-view> inside)
        /:recipe (view_recipe.html)

What I want to achieve is an animation effect on the sub-menu when going between different recipes. The problem is that the sub-menu is rendered from scratch when changing a recipe. This is strange, as it does not need to change, because the part of the url responsible for picking up recipes.html does not change. In AngularJS, as expected, the sub-menu is not rendered again. Is this a bug in AngularDart, or the expected behavior?

@vicb
Copy link
Contributor

vicb commented Apr 2, 2014

dart-archive/route.dart#25 ?
/cc @pavelgj

@michalpie
Copy link
Author

Yes, it is similar issue, but the problem, in this case is even more general: I would expect that the parent view does not reload when the child view changes. e.g. in the following case, when we want to be able not only to change recipe id, but also how it is displayed.

Routes:
    /home (template home.html)
    /recipes (template recipes.html with a sub-menu and <ng-view> inside)
        /:recipe/view (view_recipe.html)
        /:recipe/photo (photo_recipe.html)
        /:recipe/edit (edit_recipe.html)

@pavelgj
Copy link
Contributor

pavelgj commented Apr 2, 2014

What you describe should not be happening. I created a simple example, but unable to reproduce what you're seeing:

class MyAppModule extends Module {
  MyAppModule() {
    value(RouteInitializerFn, (r, views) {
      views.configure({
        'recipes': ngRoute(
            path: '/recipes',
            view: 'recipes.html',
            mount: {
              'view': ngRoute(
                  path: '/:recipeId/view',
                  view: 'view.html'),
              'edit': ngRoute(
                  path: '/:recipeId/edit',
                  view: 'edit.html')
            })
      });
    });
    type(CountingCtrl);
    factory(NgRoutingUsePushState,
        (_) => new NgRoutingUsePushState.value(false));
  }
}

int __count = 0;

@NgController(selector: '[counting-ctrl]', publishAs: 'ctrl')
class CountingCtrl {
  final int count = __count++;
}

recipes.html

<h1>Recipes</h1>
<ul>
  <li><a href="#/recipes/123/view">Recipe 123</a>
  <li><a href="#/recipes/124/view">Recipe 124</a>
  <li><a href="#/recipes/125/view">Recipe 125</a>
  <li><a href="#/recipes/126/view">Recipe 126</a>
</ul>
<div counting-ctrl>count: {{ctrl.count}}</div>
<ng-view></ng-view>

view.html

<h2>View</h2>
<div counting-ctrl>count: {{ctrl.count}}</div>

When I click on the links I see that the count inside view.html changes, while count in recipes.html remains at 0, which means that recipes view is not recreated.

Can you share a repro?

@michalpie
Copy link
Author

We made some investigations and now we understand what's happening. In fact, your case works properly, however our case is slightly different. Our application is dynamically built from components, the organization of components is read from a server in an AJAX call at the start of the application. Based on the JSON response we then configure our router and also particular views must also be built according to this JSON result. So when configuring routes, we do not have static files with HTML templates that we could pass as views. Instead, we create just one template, dyn_view.html, with a dynamic-view component inside. The dynamic-view's responsibility is to put to the DOM actual contents. How the dynamic-view knows what to put inside? It looks at the JSON data passed to it through additional routing parameters, which are added "behind the scenes". And these additional parameters added to the route event cause the parent view to be reloaded each time even when only child view changes.

We do not know how to solve this. Ideally, we would like to be able to pass a template during route configuration instead of just a template URL, e.g.

'recipes': ngRoute(
        path: '/recipes',
        view: '<h3>Recipes</h3><ul>...</ul>',
        ....

dyn_view.html

<div>
  <dynamic-view></dynamic-view>
</div>

recipes.dart

import 'package:angular/angular.dart';
import 'package:angular/routing/module.dart';
import 'package:route_hierarchical/url_template.dart';
import 'package:di/di.dart';
import 'package:logging/logging.dart';
import 'dart:html';

class DynViewFactory {
  RouteViewFactory _viewFactory;
  DynViewFactory(this._viewFactory);
  call(model) {        
     return (RouteEvent event) {
       event.parameters['recipe_items'] = model;
       _viewFactory('dyn_view.html')(event);
     };
    }
}

class MyAppModule extends Module {
  MyAppModule() {
    value(RouteInitializerFn, (r, views) {
     DynViewFactory dynViews = new DynViewFactory(views);
     r.root
      ..addRoute(
        name: 'recipes',
        path: '/recipes',
        //enter: views('recipes.html'),
        enter: dynViews({'#/recipes/123/view' : 'Recipe 123',
                         '#/recipes/124/view' : 'Recipe 124',
                         '#/recipes/125/view' : 'Recipe 125'}),
        mount: (Route route) => route
          ..addRoute(
            name: 'view',
            path: '/:recipeId/view',
            enter: views('view.html'))
          ..addRoute(
            name: 'edit',
            path: '/:recipeId/edit',
            enter: views('edit.html')));
    });
    type(CountingCmp);
    type(DynamicView);
    factory(NgRoutingUsePushState,
        (_) => new NgRoutingUsePushState.value(false));
  }
}

int __count = 0;

@NgComponent(selector: 'counting', publishAs: 'ctrl', template: '<div counting-ctrl>count: {{ctrl.count}}</div>')
class CountingCmp {
  final int count = __count++;

  RouteProvider routeProvider;
  CountingCmp(this.routeProvider) {
    var r = routeProvider.route;
    var rn = routeProvider.routeName;
  }
}


@NgComponent(
    selector: 'dynamic-view',
    template: '<div></div>',
    publishAs: 'ctrl'
)
class DynamicView implements NgShadowRootAware {
  RouteProvider provider;
  Scope scope;
  Injector injector;
  ViewCache viewCache;
  DirectiveMap directiveMap;
  DynamicView(this.provider, this.scope, this.injector, this.viewCache, this.directiveMap);

  @override
  void onShadowRoot(ShadowRoot shadowRoot) {
    Map items = provider.route.parameters['recipe_items'];

    String li = '';
    items.forEach((k, v) {
      li += '<li><a href="$k">$v</a>';
    });

    String template = '''
        <h1>Recipes</h1>
        <ul>$li</ul>
        <counting></counting>
        <ng-view></ng-view>''';

    Scope childScope = scope.createChild({});
    Injector childInjector = injector.createChild([new Module()..value(Scope, childScope)]);
    ViewFactory viewFactory = viewCache.fromHtml(template, directiveMap);
    View view = viewFactory(childInjector);

    shadowRoot.querySelector("div").nodes.addAll(view.nodes);
  }
}


void main() {
  Logger.root.level = Level.FINEST;
  Logger.root.onRecord.listen((LogRecord r) { print('[${r.loggerName}] ${r.level}: ${r.message}'); });
  ngBootstrap(module: new MyAppModule());
}

view.html

<h2>View</h2>
<counting></counting>

@michalpie
Copy link
Author

This issue now probably can be closed: #425 solves all the problems and the workaround described above is no longer needed. Thanks a lot!

@vicb
Copy link
Contributor

vicb commented Apr 25, 2014

Thanks for the feedback

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Development

No branches or pull requests

4 participants