rendrjs / rendr

Render your Backbone.js apps on the client and the server, using Node.js.
MIT License
4.09k stars 312 forks source link

Client side rendering problem #257

Open yamsellem opened 10 years ago

yamsellem commented 10 years ago

Hi,

I've successfully split my webpage into views, successfully used express webservices (same server) to hydrate those views. So far so good (based on the last sample, rendr 0.5.0-rc1)

I got a problem when I try to refresh the page client side. I've tried

events: {
  'keyup input': 'search'
},
search: _.debounce(function (e) {
  var term = $(e.currentTarget).val();
  this.app.router.navigate('/search/' + term, {trigger: true});
}, 300)

But I got: Cannot find module 'rendr/shared/base/view' in the console.

What is the problem? How am I supposed to navigate through pages in the client side?

The application is a search application. The page is fully rendered server side on first access (say on /search/superman). Then the user can fill an input, and the results have to be fetched and re-rendered.

Navigating to the search path seems the simplest solution because - I presume - the controller will take control and to the job. I was wondering: is only the elements who have changed will be re-rendered?

spikebrehm commented 10 years ago

The proper way to redirect to another page (using pushState) from a few would be:

this.app.router.redirecTo('/search/' + term);

Not sure why that module error is happening -- try redirectTo() and see if it's fixed.

yamsellem commented 10 years ago

Thanks for your help: but redirectTo do not fix the problem :-(.

This error seems pretty hard to me, maybe I can resolve it with a better sample. Is there a complete sample with server rendereing & client rendering in it? The airbnb sample only include the former.

Thanks again. ps. maybe editing the existing sample with client side rerendering will show the problem...

yamsellem commented 10 years ago

The error comes from rendr-handlebars/shared/helpers line 16:

// it's lazy loaded, not a compile time dependency
// hiding it from r.js compiler
var lazyRequire_baseView = 'rendr/shared/base/view';
BaseView = BaseView || require(lazyRequire_baseView);

And require('rendr/shared/base/view') do not work in the browser console. Is there something to do to export it? (I don't see anything like this in the browserify task)

alexindigo commented 10 years ago

At this point you should have your BaseView populated. It would be interesting to see your pages, can you share it?

alexindigo commented 10 years ago

Hehe, I was able to reproduce it. :) Seems like Browserify being grumpy. Investigating.

@spikebrehm Just right in examples (so far tested 00 and 01) go to Users page and click on top user (in my case it's mojombo). And I tried to call require in the console, like require('underscore') – getting same error.

alexindigo commented 10 years ago

Seems like adding following to the aliasMapping fixes the problem:

          {
            cwd: 'node_modules/rendr/client/',
            src: ['**/*.js'],
            dest: 'rendr/client/'
          },
          {
            cwd: 'node_modules/rendr/shared/',
            src: ['**/*.js'],
            dest: 'rendr/shared/'
          }
alexindigo commented 10 years ago

So, it looks very interesting:

Example with backbone from console:

> require('backbone')
Error: Cannot find module 'backbone'
> require(70)
Object {VERSION: "1.0.0", $: function, noConflict: function, emulateHTTP: false, emulateJSON: false…}

And each module that requires backbone has similar hash attached:

{"async":69,"backbone":70,"jquery":"+sL++I","underscore":71}
yamsellem commented 10 years ago

All my console errors are gone :) thanks.

But, I still have a problem: the redirectTo does not trigger a fetch. This is very odd, but the following code, client side, got the right params (when console logged, is it what I expect) and the wrong result (always the initial one).

And, most of all, the network pane do not show any activity.

index: function (params, callback) {
  var spec = {
    model: {model: 'search', params: params}
  };
  this.app.fetch(spec, function (err, result) {
    callback(err, result);
  });
}

Any idea? Thanks again for your great help.

ps. is there an older version of rendr with a sample to start with (to avoid those problems)?

yamsellem commented 10 years ago

I'm one step closer:

this.app.fetch(spec, {readFromCache: false}, function (err, result) {})

This line, do the job. Still, it builds a url like http://localhost:3030/api/-/search?q=dark How can I get rid of /api/-/ ? I only need http://localhost:3030/search?q=dark

Thanks again. ps. I don't quite understand why, by default, fetchs are cached.

yamsellem commented 10 years ago

Ok, I've fixed this as well:

rendr.createServer({
  apiPath: '/ws', 
  dataAdapterConfig: {
    'default': {
      host: 'localhost:' + port + '/ws/-',
      protocol: 'http'
    }
  }
})

and mounting the webservices on /ws/-/search which seems very strange to me — why don't I have the choice to include, or not this -?).

One last thing: on the fetch success, nothing happens, the page is not redraw. Have I to listen to the model and refresh it be myself?

stevenmhunt commented 10 years ago

I was struggling with client side navigation as well, but then I discovered something really silly I had neglected: In your __layout.hbs, make sure that you have something like this:

<div id="content">{{{body}}}</div>

If you don't have an "content" div in your template,when jQuery tries to write the new view to the screen, it won't work! Server side rendering will continue to work fine, but client side will do nothing, even though your controllers and views will be executing as expected. Very frustrating! Hopefully this helps others experiencing this sort of issue.

yamsellem commented 10 years ago

I'll try that, you're feedback ring a bell to me.

ps. I've lost hope since my last attempt :-(