Magento 2 Development 11: CSS and JS

0

 

We have been learning magento for pretty long time but I have not yet introduced you to css and javascript. In this blog we will see how to use css and js in magento.

CSS

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

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

<div class="blog-container">
<form class="blog-form" method="post" action=<?php echo $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.


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

<?php
$blog = $block->getBlog();
?>
<div class="blog-container">
<form class="blog-form" method="post" action=<?php echo $block->getUrl('blog/manage/save'); ?> data-mage-init='{"validation": {}}'>
<input type="hidden" name="id" value="<?= $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="<?= $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"><?= $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 are created under view/{areacode}/web folder. And it’s convention to create separate folders for js and css files under 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

.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

<?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. src=”Webkul_BlogManager::css/style.css” this line 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

<?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 cache. You may need to hard-refresh, if you change in css because browser’s try to store css and js files locally.

JavaScript aka JS

Magento usage requirejs to manage dependencies and optimise 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

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 purpose in js just like __(“”) in php files that we have already seen.

In the second param we have passed a function which have two arguments ($, $t). It’s 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 loader in magento and once the ajax request is completed we have stopped 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 change. Please search for 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 have 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

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

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

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

<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">
<?= $blog->getId()?>
</td>
<td class="blog-list-table-title">
<?= $blog->getTitle()?>
</td>
<td class="blog-list-table-content">
<?= substr($blog->getContent(), 0, 20).'...'?>
</td>
<td class="blog-list-table-action blog-list-table-action-edit">
<a href="<?= $block->getUrl('blog/manage/edit', ['id'=>$blog->getId()])?>">
<?= __('Edit') ?>
</a>
</td>
<td class="blog-list-table-action blog-list-table-action-delete">
<a href="<?= $block->getUrl('blog/manage/delete', ['id'=>$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 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 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 later part of this blog.

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

<?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
{
public $blogFactory;
public $customerSession;
public $messageManager;
public function __construct(
Context $context,
\Webkul\BlogManager\Model\BlogFactory $blogFactory,
Session $customerSession,
\Magento\Framework\Json\Helper\Data $jsonData
) {
$this->blogFactory = $blogFactory;
$this->customerSession = $customerSession;
$this->jsonData = $jsonData;
parent::__construct($context);
}
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->jsonEncode(
[
'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 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 “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 confirmation widget so that our bloggers do not delete the blogs accidentally. Also we will add alert widget to let user 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

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 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 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 similar ui as below,

ezgif.com-gif-maker-1

The folder structure should look like,

2021-02-25_20-20

--------------------------

Post a Comment

0Comments
Post a Comment (0)
To Top