Back to Top

Magento 2 Development 14: CSS and JS

Updated 9 April 2024

Magento 2 Development 14: CSS and JS :- We have been learning Magento for a pretty long time but I have not yet introduced you to CSS and JS. In this blog, we will see how to use css and js in Magento.

CSS

Let’s first start with styling our pages. We will add styling to the add and edit page. But before that let’s add some div blocks and classes in our phtml files so that we can target them in css file.

Let’s edit the add page, view/frontend/templates/add.phtml

Updated code for view/frontend/templates/add.phtml file

<div class="blog-container">
    <form class="blog-form" method="post" action=<?= $escaper->escapeUrl($block->getUrl('blog/manage/save')); ?> data-mage-init='{"validation": {}}'>
        <div class="blog-form-field-container">
            <span class="blog-form-field-label"><?=__('Title')?>:</span>
            <input type="text" name="title" class="required-entry blog-form-field"/>
        </div>
        <div class="blog-form-field-container">
            <span class="blog-form-field-label"><?=__('Content')?>:</span>
            <textarea name="content" class="required-entry validate-no-html-tags blog-form-field"></textarea>
        </div>
        <div class="blog-form-action-container">
            <button type="submit" class="blog-form-action"><?=__('Submit')?></button>
        </div>
    </form>
</div>

Here as you can see we have added everything in nice html format and added classes to every element. And also we have made the static contents translatable.

Searching for an experienced
Magento 2 Company ?
Find out More

Similarly, we will need to change the edit page, view/frontend/templates/edit.phtml

Updated code for view/frontend/templates/edit.phtml file

<?php
$blog = $block->getBlog();
?>
<div class="blog-container">
    <form class="blog-form" method="post" action=<?= $escaper->escapeUrl($block->getUrl('blog/manage/save')); ?> data-mage-init='{"validation": {}}'>
        <input type="hidden" name="id" value="<?= $escaper->escapeHtml($blog->getId())?>"/>
        <div class="blog-form-field-container">
            <span class="blog-form-field-label"><?=__('Title')?>:</span>
            <input type="text" name="title" class="required-entry blog-form-field" value="<?= $escaper->escapeHtml($blog->getTitle()); ?>"/>
        </div>
        <div class="blog-form-field-container">
            <span class="blog-form-field-label"><?=__('Content')?>:</span>
            <textarea name="content" class="required-entry validate-no-html-tags blog-form-field" rows="10"><?= $escaper->escapeHtml($blog->getContent()); ?></textarea>
        </div>
        <div class="blog-form-action-container">
            <button type="submit" class="blog-form-action"><?=__('Submit')?></button>
        </div>
    </form>
</div>

Now we have to create the styling file. All the css and js files must be created under view/{areacode}/web folder. And it’s a convention to create separate folders for js and css files under the web folder. Also, it’s recommended to create only one css file for a module.

So we will create the css file as view/frontend/web/css/style.css

Code for view/frontend/web/css/style.css file

.blog-form .blog-form-field-container {
    margin: 15px auto;
}
.blog-form .blog-form-action-container {
    text-align: center;
}
.blog-form .blog-form-action-container .blog-form-action {
    min-width: 150px;
    padding: 10px;
    font-size: 16px;
    background: #2b4c8a;
    color: white;
}

Now still we have one more thing to do. We need to tell Magento to use this CSS file on the edit and add blog pages. For that, we need to edit their layout files.

Let’s first change the add blog page’s layout file, view/frontend/layout/blogmanager_manage_add.xml

Updated code for view/frontend/layout/blogmanager_manage_add.xml file

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <update handle="customer_account"/>
    <head>
	<css src="Webkul_BlogManager::css/style.css"/>
    </head>
    <body>
        <referenceContainer name="content">
            <block class="Magento\Framework\View\Element\Template" name="blogmanager.blog.add" template="Webkul_BlogManager::add.phtml" />
        </referenceContainer>
    </body>
</page>

Here we have added <head> node and in that, we have added <css> node. The line src=”Webkul_BlogManager::css/style.css” represents the source of the css file. Here we need to mention the css file path relative to the view/frontend/web folder.

Now similarly we need to change the edit blog page’s layout file, view/frontend/layout/blogmanager_manage_edit.xml

Updated code for view/frontend/layout/blogmanager_manage_edit.xml file

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <update handle="customer_account"/>
    <head>
		<css src="Webkul_BlogManager::css/style.css"/>
	</head>
    <body>
        <referenceContainer name="content">
            <block class="Webkul\BlogManager\Block\Blog" name="blogmanager.blog.edit" template="Webkul_BlogManager::edit.phtml" />
        </referenceContainer>
    </body>
</page>


Now if you check the pages you will find that the style is getting applied.

2021-02-24_19-25

Please make sure to compile and flush the cache. You may need to hard-refresh if you change in css because browsers try to store css and js files locally.

If then also your css is not applied please run the following Magento commands.

php bin/magento setup:static-content:deploy

JavaScript aka JS

Magento usage requirejs for managing dependencies and optimizing the loading time.

We will change the delete process and use ajax to delete the blogs. I will just show you basic js codes here. Please search and study the concepts of js in Magento on your own.

Let’s first create the js file, view/frontend/web/js/bloglist.js

Code for view/frontend/web/js/bloglist.js file

define([
    "jquery",
    'mage/translate'
], function ($, $t) {
    $('.blog-list-table .blog-list-table-action.blog-list-table-action-delete a').on('click', function(e){
        var self=this;
        e.preventDefault();
        var url = $(self).attr('href');
        $.ajax({
            type: "GET",
            dataType: "json",
            url: url,
            data: {},
            beforeSend: function() {
                $('body').trigger("processStart");
            },
            success: function (response) {
                $('body').trigger("processStop");
                $(self).closest('.blog-list-table-row').remove();
            },
            error: function (response) {
                $('body').trigger("processStop");
            }
        })
    })
});

We will use the same structure in almost every js file, so don’t get overwhelmed. In the first param of define, we are mentioning our requirements.

Here we have asked for jquery and mage/translate libraries, and the requirejs will load these libraries. The “mage/translate” is used for translation purposes in js just like __(“”) in php files that we have already seen.

In the second param, we have passed a function that has two arguments ($, $t). It corresponds to the libraries that we have requested.

That means we can access jQuery with $ and the translation library with $t. Please do not get confused, I have not used the translation library here but we will use it later.

Here we have used $(‘body’).trigger(“processStart”) which will start the loader in Magento and once the ajax request is completed we have to stop the loader with $(‘body’).trigger(“processStop”).

And almost everything else inside the anonymous function should be familiar to you because it is just jquery.

Here we have used define but we can also use require. The define is used when we want the js to be used in another js file.

For that, we have to do minor syntax changes. Please search for the difference between define and require, there are multiple blogs about them.

In Magento 2, we create aliases for the js files. For that we use requirejs-config.js, this config file has many other responsibilities too. But for now, we will just create the alias for our js file. The configuration file should be in view/frontend/requirejs-config.js

Code for view/frontend/requirejs-config.js file

var config = {
    map: {
        '*': {
            bloglist: 'Webkul_BlogManager/js/bloglist',
        }
    }
};

The alias is bloglist for our js/bloglist.js file which is the relative path from the view/frontend/web/ folder.

Now let’s edit our list page to use this javascript, in view/frontend/templates/list.phtml

Updated code for view/frontend/templates/list.phtml file

<div class="blog-list-table-container">
    <table class="blog-list-table">
        <tr class="blog-list-table-head">
            <th class="blog-list-table-id">
                <?= __("Id")?>
            </th>
            <th class="blog-list-table-title">
                <?= __("Title")?>
            </th>
            <th class="blog-list-table-content">
                <?= __("Content")?>
            </th>
            <th class="blog-list-table-action-edit">
                <?= __("Edit")?>
            </th>
            <th class="blog-list-table-action-delete">
                <?= __("Delete")?>
            </th>
        </tr>
        <?php
        $blogs = $block->getBlogs();

        foreach ($blogs as $blog) {?>
        <tr class="blog-list-table-row">
            <td class="blog-list-table-id">
                <?= $escaper->escapeHtml($blog->getId())?>
            </td>
            <td class="blog-list-table-title">
                <?= $escaper->escapeHtml($blog->getTitle())?>
            </td>
            <td class="blog-list-table-content">
                <?= substr($escaper->escapeHtml($blog->getContent()), 0, 20).'...'?>
            </td>
            <td class="blog-list-table-action blog-list-table-action-edit">
                <a href="<?= $escaper->escapeUrl($block->getUrl('blog/manage/edit', ['id'=>$escaper->escapeHtml($blog->getId())]))?>">
                    <?= __('Edit') ?>
                </a>
            </td>
            <td class="blog-list-table-action blog-list-table-action-delete">
                <a href="<?= $escaper->escapeUrl($block->getUrl('blog/manage/delete', ['id'=>$escaper->escapeHtml($blog->getId())]))?>">
                    <?= __('Delete') ?>
                </a>
            </td>
        </tr>
        <?php } ?>
    </table>
</div>

<script type="text/x-magento-init">
    {
        "*": {
            "bloglist": ""
        }
    }
</script>

Here to call the js we need to use a script tag. We do not need to mention the full path of the js file, only the alias name will suffice. Instead of * we can give an element to bind the js to that element.

Also, we can pass JSON object to the js file by writing “bloglist”: {“msg”:”Hello”}. But let’s not make this complicated.

I have also done some minor changes here like I have added classes and removed the confirm widget because we will implement it in the Magento way in the later part of this blog.

We will also need to edit our delete action Controller/Manage/Delete.php

Updated code for Controller/Manage/Delete.php file

<?php
namespace Webkul\BlogManager\Controller\Manage;

use Magento\Customer\Controller\AbstractAccount;
use Magento\Framework\App\Action\Context;
use Magento\Customer\Model\Session;

class Delete extends AbstractAccount
{
    /**
     * @var \Webkul\BlogManager\Model\BlogFactory
     */
    protected $blogFactory;

    /**
     * @var Magento\Customer\Model\Session
     */
    protected $customerSession;

    /**
     * @var \Magento\Framework\Serialize\Serializer\Json
     */
    protected $jsonData;

    /**
     * Dependency Initilization
     *
     * @param Context $context
     * @param \Webkul\BlogManager\Model\BlogFactory $blogFactory
     * @param Session $customerSession
     * @param \Magento\Framework\Serialize\Serializer\Json $jsonData
     */
    public function __construct(
        Context $context,
        \Webkul\BlogManager\Model\BlogFactory $blogFactory,
        Session $customerSession,
        \Magento\Framework\Serialize\Serializer\Json $jsonData
    ) {
        $this->blogFactory = $blogFactory;
        $this->customerSession = $customerSession;
        $this->jsonData = $jsonData;
        parent::__construct($context);
    }

    /**
     * Provides content
     *
     * @return Magento\Framework\Controller\Result\Redirect
     */
    public function execute()
    {
        $blogId = $this->getRequest()->getParam('id');
        $customerId = $this->customerSession->getCustomerId();
        $isAuthorised = $this->blogFactory->create()
                                    ->getCollection()
                                    ->addFieldToFilter('user_id', $customerId)
                                    ->addFieldToFilter('entity_id', $blogId)
                                    ->getSize();
        if (!$isAuthorised) {
            $msg=__('You are not authorised to delete this blog.');
            $success=0;
        } else {
            $model = $this->blogFactory->create()->load($blogId);
            $model->delete();
            $msg=__('You have successfully deleted the blog.');
            $success=1;
        }     
        $this->getResponse()->setHeader('Content-type', 'application/javascript');
        $this->getResponse()->setBody(
            $this->jsonData->serialize(
                    [
                        'success' => $success,
                        'message' => $msg
                    ]
                ));
    }
}

This code is pretty self-explanatory. Instead of redirecting we have returned JSON data to our ajax request. Here we are returning two variables, success flag, and message.

We have also removed the message manager’s message because those message gets displayed only when the page reloads.

Now if you load the blog list page and click on the delete button. You should see similar results as below,

ezgif.com-gif-maker

For some of you, the static files i.e. css and js files might not get updated even after cache flush. That’s because Magento stores the static files inside the “pub/static” folder. To regenerate these static files we need to run the static content deployment command,

php bin/magento setup:static-content:deploy -f

And in some extreme cases, we need to delete the contents of the pub/static folder before running this command. Just make sure that you do not delete the pub/static/.htaccess file.

JS widgets

Now let’s add a confirmation widget so that our bloggers do not delete the blogs accidentally. Also, we will add an alert widget to let users know that the blog was deleted successfully.

We will use the Magento versions of these widgets. Magento provides many widgets but we mostly use alert, confirm, and modal widgets.

Let’s edit the js file view/frontend/web/js/bloglist.js

Updated code for view/frontend/web/js/bloglist.js file

define([
    "jquery",
    'Magento_Ui/js/modal/confirm',
    'Magento_Ui/js/modal/alert',
    'mage/translate'
], function ($, confirmation, alert, $t) {
    $('.blog-list-table .blog-list-table-action.blog-list-table-action-delete a').on('click', function(e){
        var self=this;
        e.preventDefault();
        confirmation({
            title: $t('Delete?'),
            content: $t('Are you sure you want to delete this blog?'),
            actions: {
                confirm: function(){

                    //If confirmed
                    var url = $(self).attr('href');
                    $.ajax({
                        type: "GET",
                        dataType: "json",
                        url: url,
                        data: {},
                        beforeSend: function() {
                            $('body').trigger("processStart");
                        },
                        success: function (response) {
                            $('body').trigger("processStop");
                            $(self).closest('.blog-list-table-row').remove();
                            alert({
                                content: response.message
                            });
                        },
                        error: function (response) {
                            $('body').trigger("processStop");
                            alert({
                                content: $t('Something went wrong.')
                            });
                        }
                    })
                    
                }
            }
        });
    })
});

Please notice the changes from the last version of this file. We have requested alert and confirm widgets in define. Please check out their syntax in the Magento devdoc.

Basically, the confirm function inside the action will get executed when the user clicks on the OK button of the confirm widget. So we have written all the ajax code in that block.

We have also used the alert widget in the success and error section of the ajax request to show the message from the server and an error message respectively. This alert is not the same as the javascript’s alert message.

We have also used the translation library with $t to make the static content translatable.

Now if you try to delete you should get a similar UI as below,

ezgif.com-gif-maker-1

The folder structure should look like this,

Selection_115
Folder Structure

Next Blog -> Magento 2 Development 15: Admin Menu and Controller

Previous Blog -> Magento 2 Development 13: Deleting

. . .

Leave a Comment

Your email address will not be published. Required fields are marked*


Be the first to comment.

Back to Top

Message Sent!

If you have more details or questions, you can reply to the received confirmation email.

Back to Home