19

I'm trying to use pjax on my site, which means that for a full page request I render the whole template (this is normal behaviour), but on pjax requests I would like to render just one section. My templates all extend a master template.

How can I most elegantly do that?

duality_
  • 17,738
  • 23
  • 77
  • 95

4 Answers4

43

This is kind of an old question but I would like to throw down another solution.

Lets say you have a view layout called main.blade.php and another view that extends main called page.blade.php

main.blade.php

<!DOCTYPE html>
<html lang="en-GB">
<head>
    <title>My Title</title>

    @section('head')
        <link rel="stylesheet" href="{{ URL::asset('css/style.css') }}">
    @show
</head>
<body>

    @yield('content')

    @section('footer')
        <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
        <script type="text/javascript" src="{{ URL::asset('js/main.js') }}"></script>
    @show
</body>
</html>

page.blade.php

@extends('main')

@section('content')
    <div class="container">
        This is a rendered page
    </div>
@stop

Just a simple basic template to get things started. In your controller if you return a View::make('page') you will get the complete HTML but Laravel provides a way to return specific sections. Here is an example of how to display the content you want based on if its an ajax call or not from within your controller:

my_controller.php

function get_index() {
    $view = View::make('page');

    if(Request::ajax()) {
        $sections = $view->renderSections(); // returns an associative array of 'content', 'head' and 'footer'

        return $sections['content']; // this will only return whats in the content section

    }

    // just a regular request so return the whole view

    return $view;
}

Now when you make an ajax call to the page it will only return the content section rather than the entire HTML.

Community
  • 1
  • 1
JoeMoe1984
  • 1,912
  • 3
  • 21
  • 31
  • @Inigo I just took a second look at this and decided that the way I had it before was a little sloppy in its checking. So edited the request part. – JoeMoe1984 Jul 23 '14 at 17:34
  • 1
    @user151841 I tested this on Laravel 5.2.39 and it still works as is. I don't think you would need to do anything different. – JoeMoe1984 Jun 21 '16 at 21:29
10

Why wouldn't you just replace the actual content in the rendered page using a div or html element?

I do this all the time with jQuery. I simply build my initial page and send content to my views that render sections in my master layout.

Let's say I had a left navigation column and then an article in the right column. The user clicks a button to display the next article, so that's what I want to replace.

First build the initial view from your controller

public function index()
{  
  $article = Article::first();
  Return View::make('homepage', compact('article'));
}

Now in your homepage view

@extends('layouts.master')

@section('leftNavigation')
     @include('homepageLeftNavigation')
@stop

@section('article')
   <div id="articleContent">
     @include('article') <!-- see below, you'll update this include with the js below -->
   </div>
@stop

@section('script')
@parent
{{-- <script> --}}
//in JQuery I would do something like this
$('a.nextArticle').click(function(){
   var next = $('.nextArticle').attr("id");
   var next = next + 1;
   $('#articleContent').load({{ URL::to('articles/' + next ) }};
});
@stop

Assuming you're using a resourceful controller you could use your show() function, but if you just wanted a teaser on the homepage you could easily create a new function for that as well.

In your show() or newFunctionWhateverYouCallIt() you can just get the article by id

Article::find($id);

Then send that off to a view to be rendered.

return View::make('article');

And finally the article view you called included the when you first built the page and again after updating via Jquery or pjax

     {{ $article->title }}
     {{ $article->body  }}
     {{ $article->date }}
     <a href="#" class="nextArticle" id="{{ $article->id }}">Next Article ></a>

Please note I haven't tested this code so I'm sure there are a few mistakes, but you get the general idea of the logic for updating a single section of your page.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Joshua
  • 791
  • 9
  • 24
4

My best answer right now would be to state in your view that it must only extend the master template (layout) if the request is not being called via AJAX:

@if(!Request::ajax())
    @extends('master.template')
@endif

Note, however, that this solution may not be best for your specific templates (I can only guess that). You'd need to make sure that each template/view only contains things that do not repeat, like side bars, etc. For example, if you have a content area that needs to be updated with pjax, then your view should only contain whatever should be changed.

Mike Rockétt
  • 8,947
  • 4
  • 45
  • 81
  • It's not perfect, but it's pretty close. If this works (haven't tried it yet) and no one comes with a better solution, I'll accept this answer. – duality_ Mar 01 '13 at 07:41
1

Inside your controller's action, explicitly return the partial view you wanted to render:

public function action_someajax()
{
    ...
    return View::make('mypartial', $data);
}

This would render the partial instead of the controller's layout.

Jürgen Paul
  • 14,299
  • 26
  • 93
  • 133