New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using a define getter breaks pushstate url updates. #2105

Closed
marshallswain opened this Issue Nov 30, 2015 · 2 comments

Comments

Projects
None yet
2 participants
@marshallswain
Member

marshallswain commented Nov 30, 2015

I originally posted this in donejs/donejs#434, but I've made a second test to confirm that it's a Can issue, alone.

If you include a getter in an attribute that should be serialized into the URL, the url no longer gets updated when you change the attribute.

breaking-pushstate-with-define-getter

To duplicate this issue, generate a new app and use the following snippets:

app.js

import AppMap from "can-ssr/app-map";
import route from "can/route/";
import 'can/map/define/';
import 'can/route/pushstate/';
import './routes';

const AppViewModel = AppMap.extend({
  define: {
    '*': {
      serialize: false
    },
    message: {
      value: 'Hello World!'
    },
    title: {
      value: 'test-app'
    },
    app: {
      serialize: true,
      set(value){
        console.log('app getting changed to', value);
        return value;
      },
      get(lastSetVal){
        return lastSetVal;
      }
    }
  },
  switchApp(appName){
    console.log('');
    console.log(appName);
    this.attr('app', appName);
  }
});

export default AppViewModel;

index.stache

<html>
  <head>
    <title>{{title}}</title>
    {{asset "css"}}
    {{asset "html5shiv"}}
  </head>
  <body>
    <can-import from="test-app/styles.less!" />
    <can-import from="test-app/app" export-as="viewModel" />

    <p ($click)="switchApp('first')" >Switch to the first app.</p>
    <p ($click)="switchApp('second')">Switch to the second app.</p>

    {{#switch app}}
      {{#case 'first'}}
        <h2>This is the first app.</h2>
      {{/case}}
      {{#case 'second'}}
        <h1>This is the second app.</h1>
      {{/case}}
    {{/switch}}

    {{asset "inline-cache"}}

    {{#switch env.NODE_ENV}}
      {{#case "production"}}
        <script src="{{joinBase 'node_modules/steal/steal.production.js'}}"  main="test-app/index.stache!done-autorender"></script>
      {{/case}}
      {{#default}}
        <script src="/node_modules/steal/steal.js"></script>
      {{/default}}
    {{/switch}}
  </body>
</html>
@marshallswain

This comment has been minimized.

Show comment
Hide comment
@marshallswain

marshallswain Nov 30, 2015

Member

I was able to duplicate this with the following example code. If you remove the get() from the page's definition, the url no longer updates, even though the route does.

I'm getting an error (can.jquery.js:1710 Uncaught RangeError: Maximum call stack size exceeded) when using can.route.map(mapInstance), but not when I use can.route.map(MapConstructor), so I've split it out into two separate can.Maps.

<!doctype html>
<html>
<head>
  <title>JS Bin</title>
  <script src="//code.jquery.com/jquery-2.1.1.min.js"></script>
  <script src="//canjs.com/release/2.3.2/can.jquery.js"></script>
  <script src="//canjs.com/release/2.3.1/can.map.define.js"></script>
  <script src="//canjs.com/release/2.3.1/can.route.pushstate.js"></script>
  <script src="//canjs.com/release/2.3.1/can.stache.js"></script>
  <script src="//canjs.com/release/2.3.1/can.fixture.js"></script>
</head>
<body>
<script type="text/stache" id="demo">
  {{#app}}
    <h1>Pushstate Links</h1>
    <a href="/first">/first</a><br/>
    <a href="/second">/second</a><br/>

    <h1>These links change route attributes</h1>
    <a href="javascript://" ($click)="setPage('third')">Third</a><br/>
    <a href="javascript://" ($click)="setPage('fourth')">Fourth</a><br/>
  {{/app}}
</script>

<script>
  var AppState = can.Map({
    define: {
      '*': {
        serialize: false
      },
      page: {
        serialize: true,
        set: function(val){
          console.log(val);
          return val;
        },
        get: function(lastSetVal){
          return lastSetVal;
        }
      }
    }
  });

  var other = new can.Map({
    setPage: function(newPage){
      can.route.attr('page', newPage);
    }
  });

  $('body').append(can.view('#demo', {app:other}));

  can.route.map(AppState);
  can.route(':page');
  can.route.ready();
</script>
</body>
</html>
Member

marshallswain commented Nov 30, 2015

I was able to duplicate this with the following example code. If you remove the get() from the page's definition, the url no longer updates, even though the route does.

I'm getting an error (can.jquery.js:1710 Uncaught RangeError: Maximum call stack size exceeded) when using can.route.map(mapInstance), but not when I use can.route.map(MapConstructor), so I've split it out into two separate can.Maps.

<!doctype html>
<html>
<head>
  <title>JS Bin</title>
  <script src="//code.jquery.com/jquery-2.1.1.min.js"></script>
  <script src="//canjs.com/release/2.3.2/can.jquery.js"></script>
  <script src="//canjs.com/release/2.3.1/can.map.define.js"></script>
  <script src="//canjs.com/release/2.3.1/can.route.pushstate.js"></script>
  <script src="//canjs.com/release/2.3.1/can.stache.js"></script>
  <script src="//canjs.com/release/2.3.1/can.fixture.js"></script>
</head>
<body>
<script type="text/stache" id="demo">
  {{#app}}
    <h1>Pushstate Links</h1>
    <a href="/first">/first</a><br/>
    <a href="/second">/second</a><br/>

    <h1>These links change route attributes</h1>
    <a href="javascript://" ($click)="setPage('third')">Third</a><br/>
    <a href="javascript://" ($click)="setPage('fourth')">Fourth</a><br/>
  {{/app}}
</script>

<script>
  var AppState = can.Map({
    define: {
      '*': {
        serialize: false
      },
      page: {
        serialize: true,
        set: function(val){
          console.log(val);
          return val;
        },
        get: function(lastSetVal){
          return lastSetVal;
        }
      }
    }
  });

  var other = new can.Map({
    setPage: function(newPage){
      can.route.attr('page', newPage);
    }
  });

  $('body').append(can.view('#demo', {app:other}));

  can.route.map(AppState);
  can.route(':page');
  can.route.ready();
</script>
</body>
</html>
@justinbmeyer

This comment has been minimized.

Show comment
Hide comment
@justinbmeyer

justinbmeyer Dec 15, 2015

Contributor

@marshallswain anyway you can get this into a CanJS test? You can see some examples in route_test that use an iframe to simulate similar situations.

Contributor

justinbmeyer commented Dec 15, 2015

@marshallswain anyway you can get this into a CanJS test? You can see some examples in route_test that use an iframe to simulate similar situations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment