RequireJS: execute jQuery code domReady - requirejs

My main.js file looks like this:
require.config({
paths: {
"jquery": "3rd_party/jquery-2.0.3.min",
"bootstrap": "3rd_party/bootstrap.min",
"handlebars":"3rd_party/handlebars",
"html5shiv":"3rd_party/html5shiv",
"modernizr":"3rd_party/modernizr",
"respond":"3rd_party/respond.min",
"jquery-ui":"3rd_party/jquery-ui-1.10.3.custom.min",
'fancybox':'3rd_party/jquery.fancybox.pack'
},
shim: {
"bootstrap": {
deps: ["jquery"]
},
"jquery-ui": {
deps: ["jquery"]
},
"fancybox": {
deps: ["jquery"]
}
}
})
requirejs(["jquery", "fancybox","controllers/" + controller,'modules/login','bootstrap','handlebars','html5shiv','modernizr','respond', 'jquery-ui'],function($,fancybox,controller,handleLogin) {
$(document).ready(function() {
if ($(window).width()>991) {
$(".sidebar").height($(".maincontent").height());
}
$(".searchresults .greencol").height($(".searchresults .article").first().height()-100);
$('.login').click(function() {
handleLogin();
return false;
})
$('.fancybox-inline').fancybox({
maxWidth : 800,
maxHeight : 600
});
$('.fancybox-document').fancybox({
width: 660,
height: 440
});
$('.fancybox-close-button').click(function() {
$.fancybox.close();
return false;
})
});
controller.init();
})
Now with everything happening, it actually takes some time to execute this bit:
if ($(window).width()>991) {
$(".sidebar").height($(".maincontent").height());
}
And the page results in a little flicker.
My only idea is to include jQuery separately in index.html and put this bit in <script>tags. But then my RequireJS setup broke.
Do you have any suggestions how to achieve this?

So you want the resizing to happen as early as possible. It's not completely clear to me how you tried to do it earlier but doing this is the way to make it happen as early as possible, and not break or bypass RequireJS:
Take the following out of your current requirejs callback:
if ($(window).width()>991) {
$(".sidebar").height($(".maincontent").height());
}
Add the following call to requirejs in front of your current call:
requirejs(["jquery"], function($) {
$(document).ready(function() {
if ($(window).width()>991) {
$(".sidebar").height($(".maincontent").height());
}
});
});
By the way, this does not need to be in a different <script> element. You can just put the new call to requirejs right in front of the one you already have. Done this way, the resizing will happen as soon as jQuery is loaded.

Related

React data router - show fallback for loader

I'm using react-router v 6.4 with createBrowserRouter to support the new data API.
I have routes that have a loader, and this loader can take 1-2 sec to get the data from the server, and I want to show a loading animation at that time.
See the following as a simple example of what I have, and a comment pointing to what I was expecting to do/find in the docs:
const router = createBrowserRouter([
{
path: '/',
element: <Layout/>,
children: [
{
index: true,
element: <Screen title="Home"/>,
},
{
path: 'materials',
loader: async () => {
return (await fetch('/api/materials')).json()
},
fallbackElement: <Loading />, // <<--- THIS IS WHAT I WAS EXPECTING TO DO
element: <Materials/>,
},
{
path: 'projects',
loader: async () => {
return (await fetch('/api/projects')).json()
},
element: <Projects/>,
},
],
},
])
Could not find how to place a "fallback" element on a route to show while the loader is waiting for the data, only to place a fallbackElement on the RouterProvider component, but that is not what I want (it shows the fallback element only on the mount of RouterProvider, not when changing between routes).
Seems kinda weird that such a thing is not supported, and cannot really find answers through the search here as well.
As per the documentation, on the component consuming the loader data you have to use React.Suspense and Await components to show the fallback, something like this:
import { Await, useLoaderData } from "react-router-dom";
function Book() {
const { book, reviews } = useLoaderData();
return (
<div>
<h1>{book.title}</h1>
<p>{book.description}</p>
<React.Suspense fallback={<ReviewsSkeleton />}>
<Await
resolve={reviews}
errorElement={
<div>Could not load reviews 😬</div>
}
children={(resolvedReviews) => (
<Reviews items={resolvedReviews} />
)}
/>
</React.Suspense>
</div>
);
}
https://reactrouter.com/en/main/components/await#await
That's in theory, because I've done that and my loaders are not showing either.

noUISlider - How to destroy the noUiSlider without deleting the element?

I have an element in my HTML and it has some HTML inside it. I am converting that to noUISlider at the click of a button (Start Slider). There is another button to hide the slider (Hide Slider). I want to hide the slider but keep the and also the HTML inside it. I am trying the slider.nouislider.destroy(); but it deletes the element completely.
Any help on this is appreciated.
Thank you.
Suhas
Okay, this is what I did and it worked for me. Hopefully, it could be helpful to someone or if there is a better way to do this, please let me know.
My HTML is
<div class="sliderContainer" id="slider">some HTML code</div>
And My JS code is
var sliderActive = false;
function createSlider() {
if (!sliderActive) {
sliderActive = true;
noUiSlider.create(slider, {
start: [360, 1080],
connect: true,
step: 15,
behaviour: 'drag',
tooltips: [
{
to: function (value) {
return processValue(value);
},
from: function (value) {
return processValue(value);
}
},
{
to: function (value) {
return processValue(value);
},
from: function (value) {
return processValue(value);
}
},
],
range: {
'min': 0,
'max': 1440,
}
});
}
}
function destroySlider() {
sliderActive = false;
$('.sliderContainer').attr('class', 'sliderContainer');
$('.noUi-base').remove();
delete slider.noUiSlider;
slider = document.getElementById('slider');
}
Thank you.

Make a hook to be executed only on "onClick" button

I have a Prestashop module, and I want to execute a hook (inserting a product) just when clicking on a button.
Here is what I'm doing right now :
In the module.php file I'm using this function :
public function hookActionProductAdd()
{
//code to create a product
}
In the module.tpl file I'm creating a button, its onClick execute the hook :
<button onclick="createProduct()">Create product</button>
And at the end of the tpl file I add the script code :
<script>
function createProduct() {
{hook h='ActionProductAdd'}
}
</script>
The problem is the hook is executed every page access/reload, and I want it to execute only when clicking on the button.
A hook is only executed at runtime, when Prestashop generates the template files. Here you want to create an ajax function in your module.
Your module files will look like this :
- mymodule.php
- ajax/
- my_module_ajax.php
- js/
- my_module.js
- views/
- templates/
- front/
- my_module_template.tpl
In file mymodule.php you've got:
<?php
if (!defined('_PS_VERSION_'))
exit;
class MyModule extends Module
{
public function __construct()
{
[...]
}
public function install()
{
if (!parent::install() || !$this->registerHook('header'))
return false;
return true;
}
public function hookHeader($params)
{
$this->context->controller->addJS(($this->_path).'js/my_module.js');
}
public function _ajax_create_product($params)
{
[...]
return $result;
}
}
In file my_module_ajax.php you've got:
<?php
require_once(dirname(__FILE__).'/../../../config/config.inc.php');
require_once(dirname(__FILE__).'/../../../init.php');
require_once(dirname(__FILE__).'/../mymodule.php');
$context = Context::getContext();
// Instance of module class
$module = new MyModule();
switch (Tools::getValue('action'))
{
case 'createProduct':
echo $module->_ajax_create_product(Tools::getValue('test'));
break;
default:
die('error');
}
In file my_module.js you've got:
$(document).ready(function(){
$(document).on('click', '#myButton', function(){
createProduct('a_value');
});
});
function createProduct(value) {
$.ajax({
type: 'GET',
url: baseDir + 'modules/mymodule/ajax/my_module_ajax.php?rand=' + new Date().getTime(),
headers: { "cache-control": "no-cache" },
async: true,
cache: false,
data: 'action=createProduct&value=' + value+ '&other=' + 'test',
success: function(data)
{
console.log("product created");
}
});
}
In file my_module_template.tpl you've got:
<button id="myButton" name="myButton">Create product!</button>
This code is not tested and should be adapted to your needs but the overall concept is here.

Masonry Overlap

I am noticing that my masonry page is creating overlap and unequal spacing. This isn't consistent and seems to happen sometimes, while at other times it works fine. In every scenario if I resize my window slightly, the mason() function kicks in and fixes it. I originally thought that it was an issue with having to wait for the images to load (around 30 at a time are loading), but I have already implemented imagesLoaded and see no difference. Can anyone point out my mistake?
<script>
function mason() {
var $container = $('#dealcontainer').masonry({
itemSelector: '.outerdeal',
columnWidth: '.outerdeal'
});
$container.imagesLoaded(function(){
$container.masonry();
});
}
function colorize()
{
$('.dealfilterli').click(function (event) {
if (event.target.type !== 'checkbox') {
$(':checkbox', this).trigger('click');
}
$("input[type='checkbox']").change(function (e) {
if ($(this).is(":checked")) {
$(this).closest('li').addClass("colorize");
} else {
$(this).closest('li').removeClass("colorize");
}
});
});
}
function InitInfiniteScroll(){
$('#dealcontainer').infinitescroll({
navSelector : "div.pagination",
nextSelector : "div.pagination li a",
itemSelector : "#deals div.outerdeal",
loading:{
finishedMsg: '',
img: 'http://www.example.com/img/icons/site/spinner.gif',
msgText: '',
speed: 'fast',
},
},function(newElements) {
var $newElems = $( newElements );
$('#dealcontainer').masonry( 'appended', $newElems );
mason();
});
}
$( document ).ready(function() {
InitInfiniteScroll();
colorize();
});
$(window).resize(function() {
InitInfiniteScroll();
mason();
}).resize();
</script>
I was having the exact same issue despite using imagesLoaded, and after a lot of trial and error I found that the problem can be solved with a setTimeout function. Here is an example from my project:
setTimeout(function() {
masonryContainer.imagesLoaded(function() {
masonryContainer.prepend(newPost);
masonryContainer.masonry('prepended', newPost);
});
}, 500);
The 500ms timeout is arbitrary, so I would play around with that on your page to find the lowest possible value that still fixes your issue. Hope that helps!
Cheers,
Jake
You should use:
$container.masonry('reloadItems');
on mason() function and everything will be replaced in the correct position.

Toastr is not displaying the way it should

toastr is showing an odd behavior -- it's being displayed in a rather ugly way, and I am not overriding anything. No options are given on how to style, but still I am getting this ugly notification.
This is what it looks like:
I am pulling toastr through requireJS; I don't know if that even matters.
logger.js
define(['durandal/system', 'toastr'], function (system, toastr) {
var logger = {
log: log,
logError: logError
};
return logger;
function log(message, data, source, showToast) {
logIt(message, data, source, showToast, 'info');
}
function logError(message, data, source, showToast) {
logIt(message, data, source, showToast, 'error');
}
function logIt(message, data, source, showToast, toastType) {
source = source ? '[' + source + '] ' : '';
if (data) {
system.log(source, message, data);
} else {
system.log(source, message);
}
if (showToast) {
if (toastType === 'error') {
toastr.error(message);
} else {
toastr.info(message);
}
}
}
});
main.js
requirejs.config({
baseUrl: '../Scripts',
paths: {
'services': '../App/services',
'viewmodels': '../App/viewmodels',
'views': '../App/views',
'config': '../App/config',
'durandal': 'durandal',
'plugins': 'durandal/plugins',
'transitions': 'durandal/transitions',
'text': 'text',
'toastr': 'toastr'
}
});
define('jquery', function () { return jQuery; });
define('knockout', ko);
define('main', ['durandal/system', 'durandal/app', 'durandal/viewLocator', 'plugins/router', 'services/logger'], function (system, app, viewLocator, router, logger) {
//>>excludeStart("build", true);
system.debug(true);
//>>excludeEnd("build");
app.title = 'Prepare to die';
app.configurePlugins({
router: true,
dialog: true,
widget: true
});
app.start().then(function () {
// Router will use conventions for modules
// assuming viewmodels/views folder structure
router.makeRelative({ moduleId: 'viewmodels' });
// Replace 'viewmodels' in the moduleId with 'views' to locate the view.
// look for partial views in a 'views' folder in the root.
viewLocator.useConvention();
// Show the app by setting the root view model for our application with a transition.
app.setRoot('viewmodels/shell', 'entrance');
// Override bad route behavior to write to
// console log and show error toast
router.handleInvalidRoute = function (route, params) {
logger.logError('No route found', route, 'main', true);
};
});
});
shell.js
define(['durandal/system', 'services/logger', 'plugins/router', 'config'],
function (system, logger, router, config) {
var shell = {
activate: activate,
router: router
};
return shell;
function activate() {
logger.log('Application is Loaded!', null, system.getModuleId(shell), true);
router.map(config.routes).buildNavigationModel();
return router.activate();
}
});
shell.html
<div>
<header>
<!-- ko compose: {view: 'navigation'} -->
<!-- /ko -->
</header>
<section id="content" class="main container-fluid">
<!-- ko compose: {model: router.activeItem, afterCompose: router.afterCompose} -->
<!-- /ko -->
</section>
</div>
Just as a sidebar, we use toastr under Durandal and I know from John Papa's writings that he feels that third-party frameworks should be loaded globally, while our own modules should be loaded modularly. Just food for thought. I can tell that switching to a global model for third-party frameworks eliminated a lot of esoteric issues.
A quick work-around fix is to do the following:
toastr.options.toastClass = 'toastr';

Resources