ReactJS talks to your API

Remove the link. In base.html.twig, we already have a few JavaScript files that are included on every page. But now, I want to include some JavaScript on just this page - I don't need this stuff everywhere.

Page-Specific JavaScript (or CSS)

Remember from earlier that those script tags live in a javascripts block. Hey, that's perfect! In the child template, we can override that block: {% block javascripts %} then {% endblock %}:

38 lines app/Resources/views/genus/show.html.twig
... lines 1 - 23
{% block javascripts %}
... lines 25 - 36
{% endblock %}

Now, whatever JS we put here will end up at the bottom of the layout. Perfect, right?

No, not perfect! When you override blocks, you override them completely. With this code, it will completely replace the other scripts in the base template. I don't want that! I really want to append content to this block.

The secret awesome solution to this is the parent() function:

38 lines app/Resources/views/genus/show.html.twig
... lines 1 - 23
{% block javascripts %}
{{ parent() }}
... lines 26 - 36
{% endblock %}

This prints all of the content from the parent block, and then we can put our cool stuff below that.

Including the ReactJS Code

Here's the goal: add some JavaScript that will make an AJAX request to the notes API endpoint and use that to render them with the same markup we had before. We'll use ReactJS to do this. It's powerful... and super fun, but if it's new to you, don't worry. We're not going to learn it now, just preview it to see how to get our API working with a JavaScript frontend.

First, include three external script tags for React itself. Next, I'm going to include one more script tag that points to a file in our project: notes.react.js:

38 lines app/Resources/views/genus/show.html.twig
... lines 1 - 23
{% block javascripts %}
{{ parent() }}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.3/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.3/react-dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
<script type="text/babel" src="{{ asset('js/notes.react.js') }}"></script>
... lines 31 - 36
{% endblock %}

Let's check that file out! Remember, it's in web/js/notes.react.js:

69 lines web/js/notes.react.js
var NoteSection = React.createClass({
getInitialState: function() {
return {
notes: []
}
},
componentDidMount: function() {
this.loadNotesFromServer();
setInterval(this.loadNotesFromServer, 2000);
},
loadNotesFromServer: function() {
$.ajax({
url: '/genus/octopus/notes',
success: function (data) {
this.setState({notes: data.notes});
}.bind(this)
});
},
render: function() {
return (
<div>
<div className="notes-container">
<h2 className="notes-header">Notes</h2>
<div><i className="fa fa-plus plus-btn"></i></div>
</div>
<NoteList notes={this.state.notes} />
</div>
);
}
});
var NoteList = React.createClass({
render: function() {
var noteNodes = this.props.notes.map(function(note) {
return (
<NoteBox username={note.username} avatarUri={note.avatarUri} date={note.date} key={note.id}>{note.note}</NoteBox>
);
});
return (
<section id="cd-timeline">
{noteNodes}
</section>
);
}
});
var NoteBox = React.createClass({
render: function() {
return (
<div className="cd-timeline-block">
<div className="cd-timeline-img">
<img src={this.props.avatarUri} className="img-circle" alt="Leanna!" />
</div>
<div className="cd-timeline-content">
<h2><a href="#">{this.props.username}</a></h2>
<p>{this.props.children}</p>
<span className="cd-date">{this.props.date}</span>
</div>
</div>
);
}
});
window.NoteSection = NoteSection;

The ReactJS App

This is a small ReactJS app that uses our API to build all of the same markup that we had on the page before, but dynamically. It uses jQuery to make the AJAX call:

69 lines web/js/notes.react.js
var NoteSection = React.createClass({
... lines 2 - 12
loadNotesFromServer: function() {
$.ajax({
url: '/genus/octopus/notes',
success: function (data) {
this.setState({notes: data.notes});
}.bind(this)
});
},
... lines 21 - 32
});
... lines 34 - 69

But I have a hardcoded URL right now - /genus/octopus/notes. Obviously, that's a problem, and lame. But ignore it for a second.

Back in the template, we need to start up the ReactJS app. Add a script tag with type="text/babel" - that's a React thing. To boot the app, add ReactDOM.render:

38 lines app/Resources/views/genus/show.html.twig
... lines 1 - 23
{% block javascripts %}
... lines 25 - 29
<script type="text/babel" src="{{ asset('js/notes.react.js') }}"></script>
<script type="text/babel">
ReactDOM.render(
... lines 33 - 34
);
</script>
{% endblock %}

PhpStorm is not going to like how this looks, but ignore it. Render the NoteSection into document.getElementById('js-notes-wrapper'):

38 lines app/Resources/views/genus/show.html.twig
... lines 1 - 31
ReactDOM.render(
<NoteSection />,
document.getElementById('js-notes-wrapper')
);
... lines 36 - 38

Back in the HTML area, clear things out and add an empty div with this id:

38 lines app/Resources/views/genus/show.html.twig
... lines 1 - 4
{% block body %}
... lines 6 - 20
<div id="js-notes-wrapper"></div>
{% endblock %}
... lines 23 - 38

Everything will be rendered here.

Ya know what? I think we should try it. Refresh. It's alive! It happened quickly, but this is loading dynamically. In fact, I added some simple magic so that it checks for new comments every two seconds. Let's see if it'll update without refreshing.

In the controller, remove one of the notes - take out AquaWeaver in the middle. Back to the browser! Boom! It's gone. Now put it back. There it is! So, really cool stuff.

Generating the URL for JavaScript

But... we still have that hardcoded URL. That's still lame, and a problem. How you fix this will depend on if you're using AngularJS, ReactJS or something else. But the idea is the same: we need to pass the dynamic value into JavaScript. Change the URL to this.props.url:

69 lines web/js/notes.react.js
var NoteSection = React.createClass({
... lines 2 - 12
loadNotesFromServer: function() {
$.ajax({
url: this.props.url,
... lines 16 - 18
});
},
... lines 21 - 32
});
... lines 34 - 69

This means that we will pass a url property to NoteSection. Since we create that in the Twig template, we'll pass it in there.

First, we need to get the URL to the API endpoint. Add var notesUrl = ''. Inside, generate the URL with twig using path(). Pass it genus_show_notes and the genusName set to name:

40 lines app/Resources/views/genus/show.html.twig
... lines 1 - 23
{% block javascripts %}
... lines 25 - 30
<script type="text/babel">
var notesUrl = '{{ path('genus_show_notes', {'genusName': name}) }}';
... lines 33 - 37
</script>
{% endblock %}

Yes, this is Twig inside of JavaScript. And yes, I know it can feel a little crazy.

Finally, pass this into React as a prop using url={notesUrl}:

40 lines app/Resources/views/genus/show.html.twig
... lines 1 - 33
ReactDOM.render(
<NoteSection url={notesUrl} />,
document.getElementById('js-notes-wrapper')
);
... lines 38 - 40

Try that out. It still works very nicely.

Go Deeper!

There is also an open-source bundle called FOSJsRoutingBundle that allows you to generate URLs purely from JavaScript. It's pretty awesome.

Congrats on making it this far: it means you're serious! We've just started, but we've already created a rich HTML page and an API endpoint to fuel some sweet JavaScript. And we're just starting to scratch the surface of Symfony.

What about talking to a database, using forms, setting up security or handling API input and validation? How and why should you register your own services? And what are event listeners? The answers to these will make you truly dangerous not just in Symfony, but as a programmer in general.

See you on the next challenge.

Leave a comment!

  • 2016-11-24 weaverryan

    Hey Xeyos!

    I've never tried it (though I've seen PhpStorm advertise this), but my guess is, yea! It should work just fine :). The reason I don't use it is that it seems that if you need Babel to transforms ES6 to ES5, then you probably will also want to go a step further and use a module bundler like webpack so that you can use require/import statements. We use webpack here on KnpU, and it takes care of the Babel transformation. But, the water seems like a great idea to me if you don't need a bundler!

    Cheers!

  • 2016-11-24 Xeyos

    Hi weaverryan,
    A simple question, you use File watcher 'Babel' whit PhpStorm?

  • 2016-10-24 weaverryan

    Awesome! Let me know if you have any questions :)

  • 2016-10-23 Andrada Ganesha

    Hello Ryan,
    I will modify the project to include webpack and prepare the environment for production. Thank you for your suggestions and support!

  • 2016-10-22 weaverryan

    Hey Andrada!

    I know you have it working now, but I wanted to answer this here, in case others wonder. So... basically... yes! In this tutorial, we use React in a *very* simple way - more simple than should be used on production. In reality, using ReactJS is a bit more complex - it really requires a "build" step and a tool like webpack (or browserify) to do that. It's a big giant topic - you may have seen a presentation I gave at Symfony.cat about this - and one that we're going to start covering here over the next few months. React and Symfony are an awesome combination... but there's a lot of stuff to learn and setup before you can really dig into it.

    Cheers!

  • 2016-10-22 weaverryan

    Great job Andrada! The problem/solution doesn't make total sense to me (what you were doing originally *sounds* correct, also), but it's not important - we're just using ReactJS here as a nice example.

    Cheers!

  • 2016-10-21 Andrada Ganesha

    Hello Ryan,

    I discovered what the problem was.
    I inserted the all the scripts including jquery and reactjs path in base.html.twig and in show.html.twig I put {{ parent() }} in the
    javascripts block, but for some reason the ajax calls were not working with this configuration.
    I updated the files and inserted jquery in base.html.twig and reactjs inserted in show.html.twig and now the code
    is working and the notes section gets loaded with the pictures showing up perfect.

    Thank you for your precious support!
    Andrada

  • 2016-10-21 Andrada Ganesha

    Hello Ryan,

    I think the ajax call is failing.
    I have all the files configured as indicated in the tutorial.
    Is it necessary to use webpack?
    I saw this in ar article related to symfony and reactjs.
    I think I am missing something.

    Thank you!
    Andrada

  • 2016-10-21 weaverryan

    Hey Andrada!

    Ok, 2 things. First, the JSON itself looks perfect, so I think that your route and controller are probably awesome :).

    Second, if you go directly to the URL you posted, you *should* just see plain JSON in your browser - without any styling or images (sure, there's an image path with avatarUri - but your browser is dumb and just shows JSON). So, that's actually ok! We do that in this chapter: https://knpuniversity.com/scre... - by the end, we just see JSON. My JSON might be a little prettier than yours, but that's thanks to a browser plugin called JsonView, which just "pretty-ifies" JSON in my browser so I can read it better).

    But, you should be able to make an AJAX call to this endpoint (we do it inside React), then use the data to update the HTML on the page (React handles this), and this updated HTML *should* be styled and show the avatar image. Are you making the AJAX call? Is that where things fail?

    Let me know!

  • 2016-10-21 Andrada Ganesha

    Hello Ryan,

    Thank you for responding on this matter.
    My url is locally on MAMP: http://blogsymfony.dev/mysymfo...
    And it just print the JSON response to the browser that is:

    {"notes":[{"id":1,"username":"AquaPelham","avatarUri":"\/mysymfony\/web\/images\/leanna.jpeg","note":"Octopus asked me a riddle, outsmarted me","date":"Dec. 10, 2015"},{"id":2,"username":"AquaWeaver","avatarUri":"\/mysymfony\/web\/images\/ryan.jpeg","note":"I counted 8 legs... as they wrapped around me","date":"Dec. 1, 2015"},{"id":3,"username":"AquaPelham","avatarUri":"\/mysymfony\/web\/images\/leanna.jpeg","note":"Inked!","date":"Aug. 20, 2015"}]}

    and no other content.
    Any idea what I am doing wrong here?

    Many thanks!
    Andrada

  • 2016-10-21 weaverryan

    Hi Andrada!

    Awesome - and nice work practicing with this stuff :). Oh, and I love FOSJsRoutingBundle - so +1 for using that!

    About your issue, the code you have and what you're describing sounds ok, so I'm guessing we have some minor issue. First, your genus_show_notes endpoint *does* return JSON, correct? And in that JSON, there is an avatarUri field for each note, correct? If so, is the problem simply that when you parse the JSON in JavaScript, the resulting HTML has a missing avatar? Or are there other issues?

    Let me know - I'm not sure that I'm fully understanding (and so answering) the *exact* issue that you're having.

    Cheers!

  • 2016-10-19 Andrada Ganesha

    Hello Ryan,

    I enjoyed the tutorial and played with it in a small app.
    Problem is I am getting just the json response printed out at /genus/show/notes endpoint, no styling or pictures.
    I am using Symfony 2.8. Should I update to Symfony 3.1.2?
    I used FOSJsRoutingBundle and in routing.yml I am declaring this:

    genus_show_notes:
    pattern: /genus/{genusName}/notes
    defaults: { _controller: AppBundle:Genus:getNotes }
    options:
    expose: true

    Can you point me in the right direction?
    What am I doing wrong?

    Thank you!

  • 2016-10-19 weaverryan

    Hey Claudio!

    Ah, you're right - my answer above is a bit too "ahead" - it applies to how your code would look after the Doctrine relations tutorial when we make the notes dynamic! For now, it should look something like this:


    // in GenusController::getNotesAction()
    // ...

    // JUST change the avatarUri value on each line
    $notes = [
    ['id' => 1, 'username' => 'AquaPelham', 'avatarUri' => $this->get('assets.packages')->getUrl('/images/leanna.jpeg'), ...],
    ['id' => 2, 'username' => 'AquaWeaver', 'avatarUri' => $this->get('assets.packages')->getUrl('/images/ryan.jpeg'), ...],
    ['id' => 3, 'username' => 'AquaPelham', 'avatarUri' => $this->get('assets.packages')->getUrl('/images/leanna.jpeg'), ...],
    ];

    Let me know if that works! This does the same thing as the {{ asset() }} function in Twig, which simply corrects for the sub-directory where you have Symfony installed (e.g. /ProjectNme/web in your case).

    Cheers!

  • 2016-10-19 Claudio

    Hey!

    I had the same problem as Maxime and I solved it by adding the "/ProjectNme/web" to the avatar Urls.
    But I would like to do it with your function of getUrl you have there. When I put it, symfony returns an error saying that the variable note was not defined.
    I assume that this ".$note->getUserAvatarFilename() is the problem, I tried by changing $note for $notes (just trying to see whether it works) but I got no positive results.

    Thanks for this amazing tutorials!
    Cheers

  • 2016-10-14 bananaaus

    A bit late in the game but I just did this course and really enjoyed it. I had the same error. This was caused by not having your data as an associative array named 'notes' in getNotesAction() of the controller.

    So, instead of :

    $data = [ $notes ];

    It has to be:

    $data = [ 'notes' => $notes ];

    This is correct in the 'finish' code.

  • 2016-10-06 weaverryan

    Yep, the JsonResponse class should do all of this for you, but if you ever use the Response object directly with json_encode, definitely don't forget to set the Content-Type header exactly as Abou said: this is what tells JavaScript how to parse the response.

    Cheers!

  • 2016-10-06 Abou

    Mybe you're using the response methode withe the json_encode function like this :
    $response = new Response(json_encode($data,JSON_UNESCAPED_UNICODE));

    In this case you should set the content type this way :
    $response->headers->set('Content-Type', 'application/json');

    If you don't the ajax response won't parse as json then notes won't be defined.

    I had the same problem and fixed it as I mentionned !

    Cheers !

  • 2016-09-30 Diego Aguiar

    You are awesome, thanks again for your help, as always it is very appreciated

    :D

  • 2016-09-30 weaverryan

    Hey Diego!

    You're to an awesome step then :). We will have a React.js tutorial with Symfony eventually, but don't wait for us :). My best advice is that there shouldn't be anything too special: just treat Symfony is your API. You can even still login with a traditional form if you want to, and allow your JS to authenticate via a normal session cookie. Or you can use a token system like we talk about in some places here.

    Also, I did a presentation on this somewhat recently - some parts of it might help: https://youtu.be/iINLewCGnuY

    Cheers!

  • 2016-09-27 Diego Aguiar

    Hello Ryan! How you doing ?

    Thank you for this tutorials, I've learnt a lot, I finally released my first project going full with symfony
    Now, I'm going to improve my UI by using React.js , but I just can't find good tutorials showing how to integrate Symfony and React.js properly

    If you know any good tutorial which you want to share, would be very helpful and appreciated, or even better if you make one!

    Thanks ;]

  • 2016-09-26 weaverryan

    Yea, we threw it in as an extra :) - there's definitely a lot more you need to know to actually use React (and jQuery would be a lot simpler for this use-case). But, really glad you liked the course, and thanks for the kind words - awesome!

    Cheers!

  • 2016-09-26 Maksym Minenko

    I'm not sure *at all* that this ReactJS lesson was needed at this stage, but in general I liked this course.
    And, Ryan, you do have a sense of humor. :) That's a gift in itself. :)
    Thank you.

  • 2016-09-23 weaverryan

    Awesome, my please :). In Drupal - as a random reference - you're able to (in PHP code) set data onto a PHP object, which is then exposed as a special JavaScript object called `drupalSettings` that is put on the Window variable. So, that's a similar idea to what you're talking about that's out in the wild already.

    Cheers!

  • 2016-09-22 Sebastián Poliak

    Ryan! Thank you for sharing this!

    Actually, the second option - in my opinion - is more cleanear than the first one. Maybe I try it in future code: we could create a Twig function to handle or *parse* the PHP variables into *windowed* JS variables...

    And with that we could use webpack too!

    Thank you again for your time, I'll bre browsing around ;)

  • 2016-09-21 weaverryan

    Yo Sebastián!

    Ah, cool! So, no, I'm not using any Twig loader with webpack - so there's nothing fancy going on between the JS and Twig in any way. In general, to pass variables from Twig to JS, I use two strategies:

    1) In "normal" JS, I usually turn the JS that's in my .js files into objects (or, at least, functions - but objects work nicely). Then, I instantiate these from Twig and pass in the variables. For example, imagine we have some ProductListWidget.js, which, maybe, displays a list of products in a carousel and also displays whether or not there is a current sale. Ultimately, my Twig JavaScript would be something like this (it's "faux code"):

    // in side a document.ready
    var productData = {{ productCollection|json }};
    var isCurrentSale = {{ isCurrentSale ? 'true' : 'false' }};

    var productListWidget = new ProductListWidget(
    productData,
    isCurrentSale
    );

    2) When I use webpack, where even the object instantiation itself is done inside an external JS file, I set window variables and read those from within my JS. However, I *only* read these window variables in *one* spot in my JS, and then I pass those variables around afterwards. I don't want to start having a bunch of buried window.VARNAME everywhere in my JS code :).

    window.productData = {{ productCollection|json }};
    window.isCurrentSale = {{ isCurrentSale ? 'true' : 'false' }};

    I hope this helps! You should be able to slowly extract the JS into external files, and then just pass in the dynamic data with a method similar to these.

    Cheers!

  • 2016-09-21 Sebastián Poliak

    Nice feedback. Currently we are using gulp, but we have a lot of twig files with javascript code and I doesn't like it.
    With webpack are you using any king of twig loader?
    In case we have to send php variables from twig to javascript, which is in your opinion the best way? Thats why we have javascript code in twig files.

  • 2016-09-21 weaverryan

    Hi Sebastián!

    Hmm, not really - the actual answer to this is to put it into an external JS file (which is much more proper anyways) and then minify/uglify that JS :). There are a lot of ways to handle that, of varying complexity, like Assetic (we talk about it in our Symfony 2 tutorial http://knpuniversity.com/scree..., Gulp (http://knpuniversity.com/scree... and also webpack, which we currently use on KnpU.com, but we don't have a screencast about this yet.

    Let me know if this helps!

  • 2016-09-21 Sebastián Poliak

    Hey! How can you minify or obsfuscate that javascript code living inside your twig file? Is there a tool or something?

  • 2016-09-13 Sheeran

    Hi, Ryan,

    Thanks for your reply.

    "Locally" here I mean run with the Apache on my own machine, but not fetch data via URLs. When I downloaded those js files and they cannot work properly, however, I debugged the faults, there're some encoding errors in *browser.js* file that causes issue, I replaced those unrecognised code, and it works as a charm now.

    P.S. I subscribed the membership already, and I enjoy other courses A LOT. I just wonder whether you are considering to product some courses around real-time concurrency operation, such as online chat room.

    Many thanks!

    Best regards,
    Sheeran

  • 2016-09-12 weaverryan

    Awesome! That's the idea! The only problem is that you couldn't commit that and deploy it in real life - since you will probably deploy your site to myexamplesite.com (without any sub-directories) and thus *not* want these in that case. My solution *should* have made it work in all cases, by effectively adding the /aqua_note/web automatically, by detecting that you're using it as a sub-directory. But, no worries - I'm glad you got it working!

    Cheers!

  • 2016-09-08 Maxime Bonin

    The link was the issue : you were right !

    to fix this I've just completed the url with /aqua_note/web before the actual url.
    $notes = [
    ['id' => 1, 'username' => 'AquaPelham', 'avatarUri' => '/aqua_note/web/images/leanna.jpeg', 'note' => 'Octopus asked me a riddle, outsmarted me', 'date' => 'Dec. 10, 2015'],
    ['id' => 2, 'username' => 'AquaWeaver', 'avatarUri' => '/aqua_note/web/images/ryan.jpeg', 'note' => 'I counted 8 legs... as they wrapped around me', 'date' => 'Dec. 1, 2015'],
    ['id' => 3, 'username' => 'AquaPelham', 'avatarUri' => '/aqua_note/web/images/leanna.jpeg', 'note' => 'Inked!', 'date' => 'Aug. 20, 2015'],
    ];

    I couldn't get your code to work, though ...

    Everything works now : thanks :)

  • 2016-09-08 weaverryan

    Hey Maxime!

    Ah, sweet! This helps immensely. Here's the deal: IF you changed your site/webserver, so that the "web/" directory if your project were the document root, then it would work. In other words, if you changed your setup so that you could simply go to http://localhost/app_dev.php/genus/octopus. The problem is - and this is my fault for being lazy / not wanting to include some ugly details - that the JSON response (as you correctly printed in the img tag) returns simple /images/leanna.jpeg. But in *your* site, this means that it is looking for the image at http://localhost/images/leanna.jpeg - NOT http://localhost/aqua_note/web/images/leanna.jpeg (as it should be).

    The problem lies in the controller that returns the JSON: it should be returning "smarter" image paths. This is how you would do that:


    // in GenusController::getNotesAction()
    // ...

    $notes[] = [
    // .. all the other keys
    // change just avatarUri to be:
    'avatarUri' => $this->get('assets.packages')
    ->getUrl('/images/'.$note->getUserAvatarFilename()),
    ];

    This uses a "service" called assets.packages, which helps your image URLs be smarter. This will return a URL that looks like /aqua_note/web/images/leanna.jpeg: that service is smart enough to know that your site is under a "aqua_note/web" directory in your site.

    Let me know if that helps... and makes (some) sense!

    Cheers!

  • 2016-09-05 Maxime Bonin

    thanks for the quick reply Ryan !

    I can load Leanna's picture OK by going to this url : http://localhost/aqua_note/web/images/leanna.jpeg

    The 'website' is at : http://localhost/aqua_note/web/app_dev.php/genus/octopus (I use XAMPP)

    the 'inspect element' of the broken picture tells me this : <img class="img-circle" src="/images/leanna.jpeg" alt="Leanna!" data-reactid=".0.1.$1.0.0"><div></div></img>

  • 2016-09-05 weaverryan

    Hey Sheeran!

    Ah, awesome! I'm *thrilled* that these are useful (and fun!) for you - that's exactly what we're hoping for :).

    So hmm, let's figure out your issue :). First, when it doesn't work, do you see any JavaScript errors in your browser's console? And also, when you switch from the cdnjs URL to the js/browser.min.js version, you said that "it stops working ,but react... can be used locally". What do you mean by "can be used locally"?

    Let me know! And we'll find the problem.

    Cheers!

  • 2016-09-05 weaverryan

    Hey Maxime!

    Hmm, if you do an "Inspect element" on the broken images, you should be able to copy the image URL or open it in a new tab. I'd like to know *exactly* what URL is used when you try to do this. Also, If you go to the Octopus Genus page, what is the URL in your browser? Is it exactly http://localhost:8000/genus/octopus? Or is it something slightly different?

    I'm sure it's something minor with image paths - we'll be able to figure it out :).

    Cheers!

  • 2016-09-04 Maxime Bonin

    The notes load at the end of the page. But the pictures of Leanna and Ryan doesn't show. (only a caption saying "Leanna!")
    images like the octopus and the aquanaut are displayed just fine ...
    got the same 'notes.react.js' than the finish version code

  • 2016-09-04 Sheeran

    Hi, Ryan,

    Well done with your colleagues, as a foreigner I am quite impressed to these tutorials, so I am planning to have the subscription for further courses, that must be great fun!
    In the end of this episode, I run the course code perfectly ok, but when I downloaded the *react.js*, *react-dom.js* and *browser.min.js* to local machine, the dynamic effect and content doesn't exist anymore. I mean, the <div id="js-notes-wrapper"></div> part.
    After my testing, I noticed that, if I change:
    <script src="https://cdnjs.cloudflare.com/a..."></script>
    into
    <script src="{{ asset('js/browser.min.js') }}"></script>
    then it stops just working, but *react.js* and *react-dom.js* can be used locally.
    I am a newbie to this, so any advice or possible solutions will be appreciated!

    Best regards,
    Sheeran

  • 2016-07-14 Victor Bocharsky

    Hey Jade!

    Could you dump the `$variable` that you passing to the `persist()` method before the `$em->persist($variable);` line? It should be a valid Doctrine entity, but maybe you have some different object or even different type of this var here.

    Cheers!

  • 2016-07-13 Jade taboada

    i tried the code but no luck it still wont work. it is also happening on this line of code below.

    $em = $this->getDoctrine()->getManager();
    $em->persist($variable);
    $em->flush();

    hope you could help :D many thanks.

  • 2016-07-12 weaverryan

    Hi Jade!

    Ah, so your built-in web server is actually terminating! That's actually quite rare! Usually this only happens when there is a "segfault" - which is a result of a bug in PHP itself or because we're doing something *so* crazy that it's completely making PHP fall over :). In this case, I'm not sure what would be causing the problem. Try changing your code to the following. Does the built-in server still terminate?

    // return an empty JsonResponse
    return new JsonResponse([]);

    Also, this shouldn't cause the server to terminate, but make sure you have your "use" statement for the JsonResponse class.

    Cheers!

  • 2016-07-11 Jade taboada

    Hello Again,

    I just wonder this line "return new JsonResponse($data);" from getNotesAction function break the symfony itself by stoping the its service then gives me this error "Built-in server terminated unexpectedly". And nice tutz :D i love it.

  • 2016-07-11 Jade taboada

    TypeError: this.props.notes is undefined

  • 2016-06-30 weaverryan

    Hey Enkhbilguun E.!

    Ah yes, great question! When we run the built-in web server, when you go to http://localhost:8000/genus/octopus/notes, it automatically knows to use app_dev.php. So, that's why it works in the tutorial :).

    But, the *real* solution is to basically do what you did - send the request to /app_dev.php/genus/octopus/notes. Of course, you don't want to hardcode this (then it would break on production!). What I would actually do is either:

    A) Use FOSJsRoutingBundle - which allows you to generate URLs from routes right inside your JS code. It's awesome!

    B) Generate the URL in Twig, set it on some global JS variable - e.g. window.notesUrl = '{{ path('...') }}'; - and access that inside of the React.js app.

    I didn't want to dive into these details this early in the Symfony series - but it's a really valid question.

    Cheers!

  • 2016-06-30 Enkhbilguun E.

    Hi Ryan,

    Thanks for the great tutorial. Can you tell me how I can set the Symfony that I am working on Development Environment?

    Unless I add /app_dev.php/ in the notes.react.js as below, it doesn't find http://domain/genus/octopus/notes.

    Thanks.

  • 2016-06-24 Victor Bocharsky

    Hey, congrats! Good work!

  • 2016-06-24 rddro

    Hey mate thank you :D Looking forward to it seems like a VERY powerful tool for managing all assets

    Got the pagination working also by transforming all the ES 6,7 stuff to ES5 and replacing the export calls
    export class App extends Component {
    constructor(props) {
    super(props);
    this.state = {
    data: [],
    offset: 0
    }
    }
    with creating a new react class like

    var App = React.createClass({
    getInitialState: function(props) {
    return {data: [],offset: 0};
    },

  • 2016-06-24 Victor Bocharsky

    Hey, @rddro !

    We do have Gulp course on KnpU, check it here.
    Yes, we plan to add "Webpack" course in the future, but I can't give you any estimates, it's only plans for now. What about browserify or other similar stuff... well, I think Webpack will be the first anyways :)

    Cheers!

  • 2016-06-24 rddro

    Hello , thanks it's working okish and updating info ^_^
    http://b5419ce9.ngrok.io/o-via...
    not too sure about the resource impact of this solution.
    <long post="" ahead=""> ->>>>>
    Have another issue do you know if there is a course on knpu that deals with implementing webpack ? Or can gulp ?? or browserify or the other millions things ?? that do the same thing as webpack. Never used these technologies before and this javascript world can get quite confusing.

    Basically what I'm trying to do now , is have symfony serve info through an API http://b5419ce9.ngrok.io/api/l...

    And on the front-end I want to render this as a react component like I did for the single product page http://b5419ce9.ngrok.io . The problem I'm having is with pagination <_< I'm trying to integrate this component

    https://github.com/AdeleD/reac... but can't figure out some things. Tried their demo , and was working ok. But not sure about the way the asset pipeline is setup.

    To my understanding there's no way to run ES6 and ES7 and import x from y or require() stuff in the browser that's why the need for a "transpiler" ?? like the babel thingie we used in this tutorial that translates ES6 and ES7 directives in browser readable code.

    The demo of this component uses webpack it seems to generate and hot reload changes to assets it seems.

    Using the browser babel core min.js , I tried to implement this for my product page by copying the react-paginate.js generated in the build folder and copying it to my assets and including it in a script tag but produces an error it doesn't like the fat arrow function handlePageClick. Also tried babel 5 and 6 but no go , even they said it's deprecated and recommend us to use server side transpiling.

    So I'm thinking of going for the same route and implement web-pack in my symfony project but not sure how to go about it couldn't find an example on how to include npm modules for use in the browser with symfony.

  • 2016-06-21 Robert

    Thank you Ryan,

    You are doing a very good job, probably one of the very few that uses psychology & words combined with enthusiasm to encourage people to build real applications in order to ultimately build confidence in development of non-trivially complex technologies.

    I have managed to figure this out. Simply by running apache web server instead of the inbuilt server.

  • 2016-06-21 Victor Bocharsky

    Hey, @rddro !

    Congrats! It looks pretty good. Don't forget to remove setInterval(this.loadNotesFromServer, 2000); from the componentDidMount(), your solution should work without it.

  • 2016-06-21 rddro

    Hello Ryan o/ Think I figured it out. The context was changed obviously >.<

    loadNotesFromServer: function() {
    var self = this;
    var socket = io.connect('http://localhost:4321');
    socket.on('message', function(message) {
    //some checks here on message ~
    $.ajax({
    url: self.props.url,
    success: function (data) {
    self.setState({notes: data.notes});
    }
    });
    });

    $.ajax({
    url: this.props.url,
    success: function (data) {
    this.setState({notes: data.notes});
    }.bind(this)
    });
    },

  • 2016-06-21 rddro

    Hello . I'm trying to implement this but I didn't go with the pusher seems a bit overkill and the commercial limitations are pretty strict so just tried a very very basic simple implementation with socket.io where i'm emitting a simple message changed when something changes.
    var socket = io.connect('http://localhost:4321');

    socket.on('message', function(message) {

    if(message=="changed")
    {
    }

    });

    The problem I'm having is I don't know how to re-render the component ? I tried in componentDidMount to call this.loadNotesFromServer without success . I'm thinking there's a way to do it via event emitter involved.

    Found this http://mwdesilva.com/posts/eve... will try to modify it for my case, hopefully can figure something out.

  • 2016-06-14 weaverryan

    Hi Robert!

    Thanks for the info! I don't *quite* understand what is and isn't working - so thanks in advance for your patience as I ask more question :)

    1) You mentioned {{ asset('image/picture.jpeg') }}. If you put this (<img src="{{ asset('image/picture.jpeg') }}"/> in your Twig template (no React, no JS), and obviously point it to a real file in your web/ directory, does the image show up?

    2) You mentioned you added the whatever.js script tag, which was a cool idea. You said that it does *not* alert. But then you said "If i include the scripts in the twig template file, it all runs smoothly together with react". Can you clarify a bit here? Under what circumstances do you include whatever.js and it does not work (e.g. did you add the script tag to the Twig template and it does not alert?) and under what circumstances *do* things work.

    Sorry for all the questions - for whatever reason, I haven't quite nailed down in my mind what is and isn't working :).

    Cheers!

  • 2016-06-12 Robert

    Hi Ryan,

    I am running the inbuilt server just as its described in the tutorial. The assets are located in the web directory, therefore I should be able to load them via {{ asset(image/picture.jpeg) }}. The problem is that it does link it, even if I were to access simply a favicon located in web directory.

    I have created JS file to test it, and used absolute paths too: <script src="{{ absolute_url(asset('js/whatever.js')) }}"></script>

    The firebug shows that it's all good:

    Host: localhost:9000
    Connection: close
    Content-Type: text/javascript; charset=UTF-8
    Content-Length: 22

    alert("Some text.");

    But there is no alert popping up nor images. If I include the scripts in the twig template file it all runs smoothly together with the react. What could be the problem?

  • 2016-06-10 weaverryan

    Hey Robert!

    Does the genus image (the octopus) load? Or do only the avatar images in the comments not load? Also, do you have the site installed in a sub-directory of some local domain (e.g. http://example.local/symfony)? The images are being loaded by just returning the path /images/ryan.jpeg - which works great, unless you have things in a subdirectory (i.e. because then it would need to be something like /symfony/images/ryan.jpg).

    But overall, the images are just static - so check out the path that's being rendered and compare it with the actual files that you have in your project.

    Let me know what you find out!

  • 2016-06-10 Robert

    Hi Ryan,

    Could you provide me explanation to why pictures may not be loading? CSS loads properly. I have cleared the cache via php bin/console cache:clear. Also JavaScript does not want to seem to load any of the react functionalities.

    Even though I have copied and pasted the code from the tutorial description both for the show template and changed ajax request. What could be the problem?

    Thank you

  • 2016-05-22 weaverryan

    Hi Alice!

    This is actually expected. The JavaScript makes an AJAX request every 2 seconds to check for new comments ( https://github.com/knpuniversi... ). This is called "polling" - it's a "cheap" way to get the "instant update" whenever someone makes a comment. In reality, if I want this type of functionality, I will usually use something like Pusher ( https://github.com/laupiFrpar/... ) to notify me instantly of when the changes happen. I would do this instead of making AJAX requests every XX seconds. But, you *are* seeing the expected behavior :).

    Cheers!

  • 2016-05-22 alice

    Thank you for your tutorials.
    I have a probleme in the last step (ReactJS talks to your API). I have creat the Json data and I can show it in my page but it never stop to do the Ajax request. Why ?

    https://drive.google.com/file/...

  • 2016-05-03 weaverryan

    Yes! That bundle is awesome!

  • 2016-05-03 Ленур

    For ajax urls I use https://github.com/FriendsOfSy... - this is very flexible :)

  • 2016-04-13 weaverryan

    Hi Michael!

    As you listed here (good details!), the problem is happening earlier - when the React app is being initialized. I think it's a small syntax issue on line 5. Try this:


    <notesection url="{notesUrl}"/>,

    The NoteSection upper-casing is important, but the real problem is that you have an extra set of quotes around {notesUrl} and the ending / is *inside* of these quotes (that last detail I believe is causing the error).

    Let me know if this helps!

    Cheers!

  • 2016-04-13 weaverryan

    Thanks for saying - cheers and keep up the good work :)

  • 2016-04-09 Michael

    I am not able to load the comments dynamically, even after replacing my base.html.twig, show.html.twig, GenusController.php,notes.react.js and main.js with the files in your finished folder? Where should I look for the mistake?

    There is one error I do have in the console:
    Uncaught SyntaxError: embedded: Unexpected token (6:59)

    4 | ReactDOM.render(

    5 | <notesection url="{notesUrl}/">,

    > 6 | document.getElementById('js-notes-wrapper');

    | ^

    7 | );

    Also if I look at genus/octopus/notes page, every forward slash is escaped by a blackslash. I don't think that should cause any issues though.

  • 2016-04-06 Agastya F. Alfath

    I've been enjoying your tutorials! Thank you!

  • 2016-03-19 richy

    Your tutorials are awesome and so well explained. Thank you and go on!!!

  • 2016-01-16 Matt W.

    I did copy your code over from the finish directory. Everything works perfectly now. I'm unable to find what the difference was. Thank you for your great videos and your response.

  • 2016-01-16 weaverryan

    Hey Matt!

    Hmm, it *is* possible that this is caused by an AJAX error from the /genus/octopus/notes endpoint. I would first debug the response that you're getting back after making the AJAX call - in the success function of notes.react.js around line 17 (success:function ()...). If the response did not contain the valid JSON (e.g. JSON with a notes key), then this would ultimately cause this.props.notes to be undefined. But, it could be something else too :). If you download the course code for this tutorial and use the "finish" directory - you could double-check and see if that works, then compare it to your code.

    Let me know how it goes!

  • 2016-01-16 Matt W.

    Firefox is giving me the error "TypeError: this.props.notes is undefined". I am not familiar with react. Do you have any suggestions?

    Thank you.