create-guten-block icon indicating copy to clipboard operation
create-guten-block copied to clipboard

Separate front-end JS

Open danztoxsmith opened this issue 5 years ago • 38 comments

This is similar to some of #37 so apologies but I thought it would be easier to create a new one for this specific request.

I've been creating a block with some JS on the front-end and to achieve this I've ejected. I think it would be nice if there was something for both front and back provided like there is for the styles, is this something you'd consider for a future version? This is the only reason I can think of for ejecting right now.

I added .editor to the original JS filenames for backend and then added new ones with the original filenames.

image

image

Awesome toolkit btw :thumbsup:

danztoxsmith avatar Jul 11 '18 13:07 danztoxsmith

@danztoxsmith thanks for that. Care to send in a PR for this. I'd be happy to accept that.

ahmadawais avatar Jul 12 '18 10:07 ahmadawais

Yep, should hopefully be able to get to this later on 👍

danztoxsmith avatar Jul 13 '18 14:07 danztoxsmith

Have you had a chance to look at this @danztoxsmith @ahmadawais? Or is someone else able tackle it?

lkhedlund avatar Aug 08 '18 23:08 lkhedlund

Sorry for the late response @lkhedlund. I haven't yet, I realised there is a bit more work to it than simply doing the above and due to being really busy with work lately I simply haven't been able to set aside any time to do it. Feel free to have a go, by all means. I guess you'd probably want to add a .frontend.js or something instead of changing the default one to .editor.js, people updating would encounter issues otherwise I guess.

Let me know how you get on, I may also have a go myself this weekend if I can find some time 👍

I haven't done any Gutenberg stuff for a little while but I'm going to be doing a lot of it soon so could do with this 😄

danztoxsmith avatar Aug 10 '18 16:08 danztoxsmith

@danztoxsmith I also came to the same realization, haha. I did fix it, but ended up rewriting a large chunk of the plugin to change how files were organized and managed, and didn't feel like it was appropriate for a pull request since I followed different conventions from the repos - more in line with what you might find in Sage.

lkhedlund avatar Aug 13 '18 23:08 lkhedlund

+1

sbuckpesch avatar Sep 18 '18 19:09 sbuckpesch

OK we gotta move this one in. Still looking for new people to join this project and help me with this?

ahmadawais avatar Sep 18 '18 23:09 ahmadawais

@ahmadawais I fixed this in my ejected and strongly modified repo. Actually it was not so complicated:

  • Rename some files
  • Add an additional entry point in webpack.dev and webpack.prod
  • Enque new script
  • Add new variable to path.js

As I'm a noob on filing pull-requests (never did that for projects someone else is owning) I did not know how create one exactly. This file https://github.com/ahmadawais/create-guten-block/blob/master/CONTRIBUTING.md did not say anything about how to create the pull request... Can you write short step-by-step instructions in the Contributing.md file I can follow? Then I create a pull-request with my solution.

sbuckpesch avatar Sep 19 '18 10:09 sbuckpesch

@sbuckpesch Start by creating a fork, by using the Fork button in the upper right hand corner of this repository. Once you have a fork, your best bet is to create a new branch off cgb's master branch, make your proposed changes and push that branch back up to Github.

Once you've done that, head back to Github and you should see a yellow-bordered notification on the repo homepage that asks if you want to create a new pull request, which is the easiest way. The other way to get there is covered here: https://help.github.com/articles/creating-a-pull-request-from-a-fork/

brianjohnhanna avatar Sep 25 '18 01:09 brianjohnhanna

Just adding my voice to this ticket, we need this.

ideanic avatar Nov 01 '18 13:11 ideanic

@ideanic feel free to create a PR and I'll be happy to accept.

ahmadawais avatar Nov 01 '18 14:11 ahmadawais

Here is a solution that works for me locally: Similar to block.js I added a frontend.js in the block and src folder.

Then, in the cgb-scripts folder, I added the frontend.js path to the path.js file and in the webpack.config files, I converted the export to an array of configs with one having the frontend.js files as entry and dist/frontend.build as output.

All that is left is to enqueue the frontend.build.js in the init.php file.

yarn start and build runs fine, haven't tried eject yet.

I made a fork and implemented the changes: https://github.com/mtthias/create-guten-block

Please note: This is not properly documented nor QA tested, I have not fully understood the codebase yet, just found a way to make this work.

Question on the side: How do I get the setup/index.js to use my modified version of cgb-scripts? It always pulls the latest one from npm.

mattcrn avatar Dec 04 '18 01:12 mattcrn

I created a PR that attempts to fix this.

#116

drdogbot7 avatar Dec 07 '18 19:12 drdogbot7

Folks can you share what is the use case you have for a just frontend only scripts. I believe that every block should work similarly on the backend and on the frontend.

ahmadawais avatar Dec 07 '18 19:12 ahmadawais

what is the use case you have for a just frontend only scripts.

Off the top of my head… Let's say I've got a simple carousel of testimonials. On the frontend I want it to just show one at a time. In the editor, that would just make it hard to work with, so I'd rather just have them all visible and stack them.

I get that we want the editor to as close to the frontend as possible, but occasionally that's going to be impractical.

drdogbot7 avatar Dec 07 '18 19:12 drdogbot7

A front-end only script might not be worth including by default, though I suspect this will be useful in practice. I get that it's not ideal.

More important is just getting scripts on the front-end AT ALL. The main block.build.js currently only loads in the editor… or am I just being dense about that??

Here's the files loaded in the editor image

Here's the frontend. The main block.build.js, isn't loaded because it's enqueued with enqueue_block_editor_assets. image

drdogbot7 avatar Dec 07 '18 19:12 drdogbot7

So, I guess if you need JS on the frontend, then the CORRECT approach would be to edit your init.php, so that your block.build.js file is enqueued in the BLOCKNAME_cgb_block_assets function, and not the BLOCKNAME_cgb_editor_assets.

I had thought that all the RegisterBlockType stuff would throw a bunch of errors outside the editor, but it doesn't seem to.

Then… to the extent that you need things to behave differently on frontend vs. editor, you can generally handle that by writing JS that doesn't suck.

Am I on the right track here?

drdogbot7 avatar Dec 08 '18 17:12 drdogbot7

In my opinion any unnecessary Javascript should be avoided in the frontend. Loading RegisterBlockType for every block when its not needed will just increase loading times. That's why I would generate a seperate frontend only js file.

I am not sure a "frontend but not editor" js file is necessary, I would leave it out for now.

mattcrn avatar Dec 09 '18 20:12 mattcrn

@mtthias when you use create-guten-block there's webpack sitting at the build process it removes any duplicate code you have and pretty sure that one reference to registering the blocks is not too much.

I have not yet investigated if that's how it is but so far it's been true. Otherwise, we'll have to create new tree shaking methods for WordPress.

ahmadawais avatar Dec 09 '18 20:12 ahmadawais

So I probably misunderstood something, but it seemed to me like @drdogbot7 is suggesting to load all editor javascript in the frontend as well. (With the enqueue_block_assets hook) So all the custom editor logic for all my custom blocks would be loaded in the frontend where it is never used. That just doesn't seem optimal.

mattcrn avatar Dec 09 '18 22:12 mattcrn

it seemed to me like @drdogbot7 is suggesting to load all editor javascript in the frontend as well.

That IS what I'm suggesting. It feels kinda wrong to me too, but I'm not getting any errors or any noticeable performance hit. To me it's preferable to ejecting. ¯_(ツ)_/¯

drdogbot7 avatar Dec 09 '18 22:12 drdogbot7

If @drdogbot7's suggested solution is the one that should be used, then the documentation should reflect that I guess.

In my opinion a a solution where the editor javascript is not loaded on the frontend would be preferable. (I think drdogbot7's pr without the frontend only part would be fine).

mattcrn avatar Dec 16 '18 10:12 mattcrn

@mtthias that's not the point. What I need here is clear data that makes a case for editor having enough JS bytes which are actually compiled (have cost) and have enough size to be separated! Otherwise, we can add chunks building.

ahmadawais avatar Dec 16 '18 11:12 ahmadawais

Ok, I see, I will take a look at this.

mattcrn avatar Dec 16 '18 11:12 mattcrn

So why does it make sense to have separate CSS for editor and front-end, but not JS? I'm tending to have much more editor-specific JS than CSS (granted uncompiled), and it seems like it would be trivial to target CSS at the editor and just enqueue one CSS file:

.edit-post-visual-editor {
  .my-block-class {
    // bla bla bla
  }
}

@ahmadawais I'm not being facetious, I'm genuinely curious! CGB and your insights have been invaluable in building my first gutenberg blocks.

drdogbot7 avatar Dec 16 '18 16:12 drdogbot7

I never worked bevor with React & Babel. Two awesome libraries nicely combined in this project. Makes it easy to dive into Gutenberg... - thx @ahmadawais

I hope I got this right. The mainproblem in this case seams to be wordpress. React is designed to build components, each component gets passed to the dom/frontend with there JS bindings & functionality. But in our case we pass the component to the editor – where we keep the HTML and lose all bindings. So it's always a dirty solution compared to the concept of React.

In the Project I'm currently working on, based on CGB, I added an interaction.js File to each Block, containing the FE-Specific JS of the block and binding the JS the classic fashion style. A src/interaction.js joins & builds these files to one file. (Interaction.js is an naming idea, it could be named bindings.js, frontend.js or something else.)

I tried to configurate a custom Loader, extracting all interaction.js-files, and building these separately – similar to the sass Files. But I don't know Babel good enough to not only cascade the interaction.js-files.

I pushed the few needed modifications to a Fork - have a look at it.

– my two cents

mi-roh avatar Dec 17 '18 09:12 mi-roh

@ahmadawais @danztoxsmith

OK, so this seems to have gotten (a lot) off-topic. @ahmadawais I have a use-case that will require the above functionality.

My current use case is that i'm creating a suite of blocks that correspond directly into components. In my basic use case, i'm adding form components, specifically a select field that requires JS to function properly.

My CGB plugin should contain all the code required for these blocks to work both in the front end and for the editor. I don't want to have to add JS to my theme to get the functionality in my block components to work. That's a violation of separation of concerns.

My solution for this would be pretty simple and if you agree, I have no problem submitting a PR.

All we need to do is create a new entry point in Webpack for front-end JS logic. This can get added into the current /src/init.php file under the plugin_name_cgb_block_assets function in an wp_enqueue_script function. To not load the file you simply remove it from the init.php file. (I wouldn't have thought it a big deal if Webpack is dealing with that one extra entry point, especially if it's just an empty file by default.

mattjbrent avatar Jan 18 '19 16:01 mattjbrent

Thank you very much, @mtthias . Your solution was exactly what I was looking for :) Hope something like this gets implemented in the main repo. I'm currently developing a slider block, and adding the whole backend JS didn't feel like a good approach.

rodrigodagostino avatar Mar 03 '19 22:03 rodrigodagostino

Hi! I had the same problem (i was doing an slider block with lightbox) and my solution was make an frontend.js file and require it (and the rest of the javascript libraries that I need) only if is not dashboard. Quite close to @mtthias proposal solution 😄

function my_block_cgb_block_assets() { // phpcs:ignore
	wp_enqueue_style(
		'my_block-cgb-style-css',
		plugins_url( 'dist/blocks.style.build.css', dirname( __FILE__ ) ),
		array( 'wp-editor' )
	);

  if(!is_admin()){ // if frontend (not wp-admin)
    wp_enqueue_script(
  		'my_block-cgb-block-js-main',
  		plugins_url( '/src/frontend.js', dirname( __FILE__ ) ),
  		array('wp-blocks', 'wp-i18n', 'wp-element', 'wp-editor', 'javascript-library1', 'javascript-library2' ),
  		false,
  		true
  	);

    wp_enqueue_script('javascript-library1', 'https://example1.com/library.min.js');
    wp_enqueue_style('css-library1', 'https://example1.com/library.min.css');

    wp_enqueue_script('javascript-library2', 'https://example2.com/library.min.js');
    wp_enqueue_style('css-library2', 'https://example2.com/library.min.css');
  }
}

// Hook: Frontend assets.
add_action( 'enqueue_block_assets', 'my_block_cgb_block_assets' );

situplastik avatar Mar 05 '19 07:03 situplastik

@ahmadawais Firstly thanks so much for creating this, it really is such an amazing thing you're doing here.

My case for the use of frontend rendered js is required with creating an accordion for example -

export default class Accordion extends Component {

constructor( props ) {
	super( ...arguments );
	this._handleClick = this._handleClick.bind(this);
}

componentDidMount() {
	this._handleClick();
}

_handleClick() {
	const acc = this._acc.children;
	for (let i = 0; i < acc.length; i++) {
		let a = acc[i];
		a.onclick = () => a.classList.toggle("active");
	}
}

render() {

	// Setup the attributes
	const { accordionTitle, accordionText, accordionAlignment, accordionFontSize } = this.props.attributes;

	return (
		<div
			style={ {

			} }
			className={ classnames(
				this.props.className,
				accordionAlignment,
				'accordion',
				'font-size-' + accordionFontSize,
			) }
			ref={a => this._acc = a} 
    onClick={this._handleClick}
		>
			{ this.props.children }
		</div>
	);
}
}

and my save function:

save: function( props ) {

	// Setup the attributes
	const { accordionTitle, accordionText, accordionAlignment, accordionFontSize, accordionOpen } = props.attributes;

	// Save the block markup for the front end
	return (
		<div>
    <Accordion { ...props }>
      <div className="accordion" >
	<RichText.Content
	tagName="p"
	lassName="accordion-title"
	value={ accordionTitle } />
        <div className="accordion-body">
	<InnerBlocks.Content />
    </div>
      </div>
    </Accordion>
  </div>
	);
}

This works in the page editor, when clicking on the Accordion title the class "active" is toggled. But then this js is not rendered on the front end.

What would be the best way to go about getting the js to render front end?

LyleBennett avatar Jun 27 '19 13:06 LyleBennett