Integrating Select2 with Magento 2 - requirejs

I'm trying to integrate Select2 with Magento2. So far I have integrated the plugin successfully but there are errors showing on the console.
What I've done:
Downloaded the select2.min.js and put it in app/design/frontend/<vendor>/<themename>/web/js/select2.min.js
Included the script in app/design/frontend/<vendor>/<themename>/Magento_Theme/layout/default_head_blocks.xml
Added this to the phtml file in script tags:
require(['jquery'],function(jquery){
jquery(document).load(function() {
jquery("#sorter2").select2();
});
});
I know I should it include it via requireJS but I can't seem to make it work.
Thanks!

You shouldn't add it in the header on every page as it's dependencies won't necessarily load. You need to add it to your themes requirejs-config here;
/app/design/frontend/<vendor>/<theme>/requirejs-config.js
In the file put this;
var config = {
paths: {
'select2': 'js/select2.min',
},
};
Now in any phtml file you can call it like this;
<script type="text/javascript">
require(['jquery','select2'],function($){
// do stuff with select
});
</script>

Something like this works:
in requirejs-config.js
var config = {
paths: {
'select2': 'SATA_SparePartsFinder/js/vendor/select2.full'
}
};
in your .phtml:
<script type="text/javascript">
require(['jquery', 'select2'], function($) {
$('#model-select').select2({language: {
noResults: function () {
return '<?= __('No results found') ?>';
}
}});
});
</script>

Related

YUI .. call one javascript function from another java script

I have started writing unit tests. I need to call a function in signup.js from another script, unittest.js. How can I do this?
Unittest.html would incorporate both scripts.
<html>
<head>
<script scr ="signup.js"></script>
<script src="unittest,js"></script>
</head>
</html>
This is signup.js, which I have to test.
YUI.use(function(Y){
demo : function(){
window.alert('hello);
}
});
unittest.js:
YUI.use(function(Y){
var abc = new Y.Test.case(
testOk : function(){
demo(); // Calling this function but not working
<Some_Assestion_Stuff_Here>
}
);
});
Your two scripts have both created a YUI sandbox. Neither sandbox share anything with the other, so you cannot achieve unit testing demo() like this.
What you can do is to register a module in signup.js and use it in unittest.js. See the following example: http://jsfiddle.net/746nq/
In signup.js, create the module:
// Create a YUI module in signup.js.
YUI.add('signup', function (Y) {
// Write your module code here, and make your module available on the Y
// object if desired.
Y.Signup = {
demo: function () {
window.alert("Demo!");
}
};
});
In unittest.js, use the module:
// Create a YUI sandbox in unittest.js and use our newly created module
YUI().use('signup', function (Y) {
Y.Signup.demo();
// assert stuff
});
Hope this helps you.

How to check the appropriate page and load the scripts accordingly using RequireJS

In my web site, I have bunch of JavaScript files, all for different use but the login page only needs the jquery and loginValidate files. When I attach my main.js, it is suppose to load only these two files by checking the conditions. How to do that?
My config file:
requirejs.config({
baseUrl:"scripts",
paths:{
//libraries
jquery :"lib/jquery-1.9.0.min",
jqueryUI :"lib/jquery-ui-1.9.2.custom.min",
underScore :"lib/underscore-min",
backBone :"lib/backbone-min",
//scripts
appInit :"js/tasklist",
loginValidate :"js/loginValidate"
},
shim:{
"underScore":{
exports: "_"
},
"backBone":{
exports:"Backbone",
deps:["underScore"]
},
"appInit" : {
deps:["jquery","jqueryUI","underScore","backBone"]
},
"jqueryUI":{
deps:["jquery"]
},
"loginValidate":{
deps:['jquery']
}
}
});
It is only needed on login page:
require(["jquery","loginValidate"], function($,validate){
how can i call the loginValidate function?
});
The loginValidate function:
define(function(){
var tasklistHandler = function (params) {
//params take care.. to validate
};
$(function(){ // calling internally the function
var paramsLoginForm = {
loginForm : $('#tlLoginForm')
}
tasklistHandler(paramsLoginForm);
});
})
Is this the correct way to do? I am using also Backbone.js to utilise some other page; how can I proceed for those pages?
The problem you have is the lack of "return" in the "factory" function (the callback function you pass to the define call in the last snippet).
Instead you need to have something like this there:
define(function(){
return function (params) {
//params take care.. to validate
};
})
And in your require call you use run the function that is returned to you by factory function:
require(["jquery","loginValidate"], function($, validate){
var paramsLoginForm = {
loginForm : $('#tlLoginForm')
}
// \|/ that is what is returned by `define`'s callback function.
validate(paramsLoginForm);
});

Crossroads and requirejs

I'm having trouble getting a route to match using crossroads with requirejs. Well, it probably has nothing to do with requirejs, just thought I'd mention it.
This is what my code looks like:
require.config({
shim: {
/* use shims to define dependencies for modules. e.g.,
'jquery.colorize': ['jquery'],
'jquery.scroll': ['jquery'],
*/
'crossroads': ['signals', 'can']
},
paths: {
"jquery": "http://code.jquery.com/jquery-1.8.2",
"can": "/scripts/can/amd/can",
"can.fixture": "/scripts/can/amd/can/util/fixture",
"signals": "/scripts/signals/signals",
"crossroads": "/scripts/crossroads/crossroads"
}
});
require(['jquery', 'crossroads', 'controllers/project'], function ($, crossroads, projectController) {
var projectRoute = crossroads.addRoute('projects', function () {
$(document).ready(function () {
console.log('projects ready');
$.when(ProjectModel.findAll()).then(function (projectResponse) {
var projects = new SortList(projectResponse);
console.log('doc ready projects=', projects);
new ProjectsControl('#projects', {
projects: projects,
defaultSort: 'priority'
});
});
});
})
console.log('matched: ', projectRoute.match(window.location.href));
});
The url that it's trying to match is http://localhost:34382/projects and the output of console.log is "matched: false"
Any suggestions?
I figured out that I need to change what I'm matching on. I've changed this line
console.log('matched: ', projectRoute.match(window.location.href));
to this
console.log('matched: ', projectRoute.match(window.location.pathname + window.location.search));
and now it works.
Also realized that you have to explicitly call crossroads.parse() somewhere for it to work (or am I missing something?)
so at the end of my require function I call this and my route is found
crossroads.parse(window.location.pathname + window.location.search);
Also, in case anyone is wondering, I'm using CanJS as the client-side MVC framework and ASP.NET MVC4 as the server-side.

RequireJS does not run data-main script before loading required modules

My project includes the following files:
./index.html
./js/main.js
./js/vendor/require.js
./js/viewmodel/vm.js
The index.html has the following relevant snippet:
<script data-main="js/main.js" src="js/vendor/require.js"></script>
<script type="text/javascript">
require(['viewmodel/vm', 'ko'],
function(viewmodel, ko) {
ko.applyBindings(viewmodel);
}
);
</script>
The js/main.js file is as follows:
var root = this;
define('jquery', ['http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.8.3.js'], function () { return root.$; });
define('ko', ['http://ajax.aspnetcdn.com/ajax/knockout/knockout-2.1.0.js'], function (ko) { return ko; });
The js/viewmodel/vm.js file...
define(['jquery', 'ko'],
function($, ko) {
return {
subject: 'world',
greeting: 'hello'
}
}
);
When you open a browser to index.html, then the browser tries to load a file called js/ko.js instead of using the module defined in main.js. It seems like the js file pointed to by the data-main attribute is not guaranteed to run before dependency resolution. This does not seem correct to me since one purpose of the data-main js file is to define require configuration (i.e. path, shim, etc). I am using require v2.1.2.
This works perfectly fine if I copy the contents of my main.js file into the script block in index.html. By "perfectly fine" I mean that it resolved ko to be a module and finds the appropriate CDN link to resolve ko instead of trying to download ./js/ko.js.
to use the data-main attribute for configuring your whole application, it is necessary that it is the single entry point for all your code.
your 2nd script block breaks this requirement by providing a 2nd entry point. since these entry points will resolve independently of each other (and asynchronously), you cannot rely on one to affect the other.
to resolve it, refactor your code in a way that provides a single entry point to your application and do your configuration via this entry point.
That's because requirejs sets the async. Attribute on the script.
The boolean async attribute on script elements allows the external
JavaScript file to run when it's available, without delaying page load
first.
This means that both scripts are loaded and evaluated parallel, so none of the two scripts can access methods or functions from the other one.
If you want to define requirejs variables in one script you mustn't load that script with require js.
For me there are three possibilities how you can solve that problem:
Add the content of main.js to your page (as you mention)
Load the main.js file without requirejs as normal script
Define the require config before loading the scripts (link to requirejs docu )
I had the same problem. The architecture of the site that i was working was components that was loading asynchronous at each part of the page.
Each component has its own html, css, and js code.
So, my solution is to keep a guard function for all the required dependency code, to protect them from running before the main javascript file:
index.html
<head>
<script type="text/javascript">
window.BeforeMainGuard = {
beforeMainLoadedFunctions: [],
hasMainLoaded: false,
guard: function( func ) {
console.assert( typeof func === 'function' );
if( this.hasMainLoaded ) {
func();
}else {
this.beforeMainLoadedFunctions.push( func );
}
},
onMainLoaded: function() {
for( var i = 0; i<this.beforeMainLoadedFunctions.length; ++i ) {
var beforeMainLoadedFunction = this.beforeMainLoadedFunctions[i];
beforeMainLoadedFunction();
}
this.beforeMainLoadedFunctions = null;
this.hasMainLoaded = true;
}
};
</script>
<script data-main="js/main.js" src="js/vendor/require.js"></script>
<script type="text/javascript">
window.BeforeMainGuard.guard( function() {
require(['viewmodel/vm', 'ko'],
function(viewmodel, ko) {
ko.applyBindings(viewmodel);
}
);
});
</script>
</head>
js/main.js
require.config({
// your config
});
require( [ 'AppLogic' ], function( AppLogic ){
AppLogic.Init();
window.BeforeMainGuard.onMainLoaded();
} );

requirejs , multipage , weird order of execution

I am using requirejs for a multipage application. I am having a weird issue with the order of execution of the main files. Here is my setup.
In all of my html pages ( This main has all the common js files that are required by all my pages)
then on each page (say page1.html):
<script data-main="main" src="require.js" />
<script>
require(['require','main'],function(require) {
require(['main-library']);
});
</script>
My main.js looks like:
require.config({
paths:{
'lib':'../lib',
'jquery': '../lib/jquery/jquery-1.7.1.min',
'jquery.iframe-transport' : '../lib/jquery.iframe-transport.min.js'
},
deps:['jquery','jquery-ui'],
shim: {
<other shim files>
},
});
require([<list of all other dependencies>], function() {
});
And the main-library.js looks like:
define('main-library',
['main',
'jquery.iframe-transport',
<other dependencies>
],
function() {
}
);
My expectation:
Was that the "main-library" will not start to load until the "main" and all the dependencies specified in the main are not loaded. I expect this since I require "main-library" in a nested require call on each page. see above.
The issue:
When my page1.html loads, it looks like the main-library.js starts loading even before the main.js has finished loading and main-library fails to get the require.config and hence the paths for the dependencies are not seen by it.
What am I doing wrong ?
Answering my own question. It was infact another require down in that page1.html that was loading the "main-library" again. So I had something like:
<script data-main="main" src="require.js" />
<script>
require(['require','main'],function(require) {
require(['main-library']);
});
</script>
...
... Other html ...
...
<script>
require(['main-library'],function(require) {
//do something.
});
</script>
This second require on main-library was the one that was failing to find the paths. Wrapping it with a require on main worked.

Resources