ionic-team / ionic-v1

The repo for Ionic 1.x. For the latest version of Ionic, please see https://github.com/ionic-team/ionic
Other
193 stars 187 forks source link

state change behavior divergent from ui-router #284

Open diegozhu opened 7 years ago

diegozhu commented 7 years ago

Description of the problem:

ui-router by default caches no controller instance every time goes to a state. But ionic ion-nav-view caches scope and controller by default. this is confusing.

What am I expecting?

would like to know why would you guys design that way and the best practice for below scene

Why would I bother this?

My case is simple:

  1. two state named view and edit using ionNavView.
    $stateProvider
      .state({
        name: 'view',
        url: 'view',
        resolve: {
          userData: ['userProfileService', function(userProfileService) {
            return userProfileService.get();
          }]
        },
        templateUrl: 'view.html',
        controller: 'ViewController as vm'
      })
      .state({
        name: 'edit',
        url: 'edit',
        resolve: {
          userData: ['userProfileService', function(userProfileService) {
            return userProfileService.get();
          }]
        },
        templateUrl: 'edit.html',
        controller: 'EditController as vm'
      });
  2. got one service named userProfileService:
    userProfileService = {
        get: xxx,
        set: xxx
    } 
  3. For the view state, user profile data is injected through resolve. when I goes to Edit state and do some edits , back to view state, the controller is cached and user profile data is not synced however resolve is executed.
  4. uiView directive of ui-router works properly.

ui-router's best practice is inject dependent data through resolve (configured from router), and every time change to a state, ui-router will create an new instance of relevant controller and run the resolves and inject them into controller. ionNavView by default caches controllers and scopes, when goes back to an exist state, the controller will not be initialized but the resolves (configured in routers) still runs (by ui-router). The resolves make no sense if their result is not injected and could not be visited by controllers(lost in memory I guess :P). And for now my work around is adding an cache: false to routers. Force to initialize controllers every time. Another way I would come up with is just keep the controllers cached and ignore the useless resolves every time when changing states (It's unbearable for me especially when resolves contacting with server end).

ui-router uiView directive (v0.4.2 line number:4200) :

        var current = $state.$current,
            name = getUiViewName(scope, attrs, $element, $interpolate),
            locals  = current && current.locals[name];

        if (! locals) {
          $element.html(initial);
          $compile($element.contents())(scope);
          return;
        }

        $element.data('$uiView', { name: name, state: locals.$$state });
        $element.html(locals.$template ? locals.$template : initial);

        var resolveData = angular.extend({}, locals);
        scope[locals.$$resolveAs] = resolveData;

        var link = $compile($element.contents());

        if (locals.$$controller) {   //NOTE HERE: every time create a new instance of controller
          locals.$scope = scope;
          locals.$element = $element;
          var controller = $controller(locals.$$controller, locals);
          if (locals.$$controllerAs) {
            scope[locals.$$controllerAs] = controller;
            scope[locals.$$controllerAs][locals.$$resolveAs] = resolveData;
          }
          if (isFunction(controller.$onInit)) controller.$onInit();
          $element.data('$ngControllerController', controller);
          $element.children().data('$ngControllerController', controller);
        }

ionic ionNavView ($ionicViewSwitcher ) line number: 4675:

          if (alreadyInDom) {
            // it was already found in the DOM, just reconnect the scope
            ionic.Utils.reconnectScope(enteringEle.scope());

          } else {
            // the entering element is not already in the DOM
            // set that the entering element should be "staged" and its
            // styles of where this element will go before it hits the DOM
            navViewAttr(enteringEle, VIEW_STATUS_STAGED);

            var enteringData = getTransitionData(viewLocals, enteringEle, registerData.direction, enteringView);
            var transitionFn = $ionicConfig.transitions.views[enteringData.transition] || $ionicConfig.transitions.views.none;
            transitionFn(enteringEle, null, enteringData.direction, true).run(0);

            enteringEle.data(DATA_VIEW, {
              viewId: enteringData.viewId,
              historyId: enteringData.historyId,
              stateName: enteringData.stateName,
              stateParams: enteringData.stateParams
            });

            // if the current state has cache:false
            // or the element has cache-view="false" attribute
            if (viewState(viewLocals).cache === false || viewState(viewLocals).cache === 'false' ||
                enteringEle.attr('cache-view') == 'false' || $ionicConfig.views.maxCache() === 0) {
              enteringEle.data(DATA_NO_CACHE, true);
            }

            // append the entering element to the DOM, create a new scope and run link
            var viewScope = navViewCtrl.appendViewElement(enteringEle, viewLocals);

            delete enteringData.direction;
            delete enteringData.transition;
            viewScope.$emit('$ionicView.loaded', enteringData);
          }

Which Ionic Version? 1.x

My System Info: Cordova CLI: 7.0.1 Ionic CLI Version: 2.1.13 Ionic App Lib Version: 2.1.7 ios-deploy version: Not installed ios-sim version: Not installed OS: Windows 7 Node Version: v4.6.0 Xcode version: Not installed

diegozhu commented 7 years ago

any help will be appreciated.