Load requirejs module with data-bind (knockout.js)? - javascript

What I want to do is load js using the data-bind attribute. I am fairly new to requirejs and knockout and I'm not sure how to go out this.
Right now I have my js split into different require modules for each type of component I have. For example, I have a file that deals with the header dropdown (header.js):
define('headerDropdown',['jquery', 'bootstrap']),function(jquery, bootstrap){
var $menu = $(".menu");
var $dropdown = $menu.find("ul");
$menu.on("click", function () {
$dropdown.toggle("fast");
});
};
What I want to do is:
<div class="header" data-bind="headerDropdown">...</div>
And load the respective js.
Most of my js modules are UI changes based on clicks (show and hiding stuff on click) but I only want the js to load is the html block is on the page.
Hopefully this makes sense!
How can I do this using requirejs and knockout?

Looks like you are mixing concepts. First let's see the define() definition (suppose the file is headerDropdown.js):
define('headerDropdown',['jquery', 'bootstrap']),function(jquery, bootstrap){
var $menu = $(".menu");
var $dropdown = $menu.find("ul");
$menu.on("click", function () {
$dropdown.toggle("fast");
});
};
Require.js does not recommend to define a module expliciting their name ('headerDropdown'); you can get the name based on the filename. That's because require has a tool for optimization of the javascript in production: you can concatenate and minimize the output JS. The optimizer uses the filename to define the module name. Please, avoid defining with name.
If you look at the code, you are requiring ['jquery'] but inside the module definition you're using the global jQuery variable. That's OK because jQuery define their module as a global variable, but the convention is to receive in the function the jquery reference:
define('headerDropdown',['jquery', 'bootstrap']),function($, bootstrap)
You are defining a module that manipulates DOM directly, which goes against the DOM update procedure of knockout. In your case, you are using a data-bing="headerDropwodn" so the headerDropdown is a bindingHandler rather than a simple module. Please check: http://knockoutjs.com/documentation/custom-bindings.html
You can load on require as you pointed on the question. You just need to change your codes:
Load in your HTML an app.js script (for example). This app.js requires knockout and your headerDropdown bindingHandler. In the function declaration you define the ko.applyBindings and that's all.
Greetings!

Related

Can only have one anonymous define call per script file

I'm creating monaco editor using loader.js but getting the error "Can only have one anonymous define call per script file" 2 times in console.
<script src="/monaco-editor/min/vs/loader.js"></script>
Code to create editor
require.config({ paths: { 'vs': '/monaco-editor/min/vs' }});
require(['vs/editor/editor.main'], function() {
monacoEditor= monaco.editor.create(document.getElementById('coding-editor'), {
value: [
'function x() {',
'\tconsole.log("Hello world!");',
'}'
].join('\n'),
language: 'javascript'
});
});
I tried to search the issue and found below related answer:
Some piece of code that you are loading is invoking define with an anonymous module id. You could:
load that code through the AMD loader (i.e. manually require it) such that the AMD loader creates the <script> tag.
load that code before the AMD loader (i.e. define will not be available to that piece of code)
unset define for the duration of evaluation of that script (i.e. if you load it with a <script> tag, then unset define before and restore it afterwards)
try to unset define.jquery, AFAIK jquery might be checking for that on the define function
This page has lot of jquery already and I understand this because of jQuery. Please help some to make me understood by example. Thanks
I had the same issue this morning and I applied the second solution.
load that code before the AMD loader (i.e. define will not be available to that piece of code)
This works because define is being called from inside jQuery anonymously, as the error says. Explained further in the require.js website, which happens to use loader function (define, require) similar to loader.js.
In my case I simply made sure to include my loader after jQuery so the defines don't collide.
I had tried to create script by tags, but got aler:'Can only have one anonymous define'
So I just overwrite it :
this.temp_define = window['define'];
head.appendChild(loaders);
window['define'] = undefined;

Cordova Plugins

Can someone explain me how does a structure of a javascript file written to execute a custom plugin work, for example I know that
exec(<success function>,<failure function>,<service>,<action>,<args>)
this function is used to call the native where service is the plugin class name and action is the method that needs to be called in that class.
What I am failing to understand is that what does this structure do for example
cordova.define("cordova/plugin/pluginName",
function(require,exports,module){
var exec = require("cordova/exec")
pluginName.prototype.methodName = function()
I am unable to understand what is happening here ?
You don't have to use cordova.define anymore, that's added automatically on plugin install
From the doc:
Do not wrap the file with cordova.define, as it is added
automatically. The module is wrapped in a closure, with module,
exports, and require in scope, as is normal for AMD modules.
var exec = require("cordova/exec") just loads the cordova.exec module into exec, if you don't do this you can call your plugin with cordova.exec(<success function>,<failure function>,<service>,<action>,<args>) instead of just doing exec(<success function>,<failure function>,<service>,<action>,<args>)
pluginName.prototype.methodName = function() just creates a methodName function for your pluginName, so it makes possible that the user can call your plugin method like pluginName.methodName()

Lazily Load CSS and JS

I have written this piece of JS and CSS loading code and I would like some advice on it. Anything some of the Javascript Gurus could possibly point out would be much appreciated. The code works, but I have not done extensive testing, because I am concerned about replacing functions in this manner.
A single javascript file containing JQuery as well as the below code will be included on all the pages. We write all the components in house and keep them very modular separated into their own folder with the corresponding JS and CSS. You can imagine starting to use for instance a dropown, dialog and a datepicker on one page would require us to add 6 includes and this quite frankly is annoying, because I want the dependencies to resolve automatically and using JSP includes could possibly make multiple calls to the same resources.
Below is the src to load a single datepicker lazily
;(function($){
//All Lazily loaded components go here
$.fn.datepicker = function(settings){
console.log("This should only be displayed once");
loadCSS("/res/component/datepicker/datepicker.css");
var elem = this;
return loadJS("/res/component/datepicker/datepicker.js",
function(){return elem.datepicker(settings)});//After Load Completion the $.fn.datepicker is replaced
//by the proper working implementation, execute it and return it so we maintain the chain
};
}(jQuery));
function loadCSS(absoluteUrl){
if(loadCSS[absoluteUrl])
return;//Css already loaded
$('<link>')
.appendTo('head')
.attr({type : 'text/css', rel : 'stylesheet'})
.attr('href', absoluteUrl);//Appending entire element doesn't load in IE, but setting the href in this manner does
loadCSS[absoluteUrl] = true;//Memoize
}
function loadJS(absoluteUrl, onComplete){
if(loadJS[absoluteUrl])
return;//Script already loaded
loadJS[absoluteUrl] = true;//Memoize
var result;
jQuery.ajax({
async : false,//Synchronized because we need to maintain the JQuery chain
type :'GET',
url : absoluteUrl,
dataType :'script',
success : function(){
result = onComplete();
}
});
return result;
}
Have you looked in to Require JS, it will send async requests for only the modules you need for a given module.
In addition, because dependencies are scoped to the callback function, namespaces clashing is less of an issue
Typically you would have:
require(["jquery", "foo", "bar"], function($, foo, bar){...});
which allows your code to remain modularized both server side, and client side, in separate locations.
Of course, you need to set up require on your server with a config (described in the webpage), and wrap your resources in define blocks:
define("foo", ["jquery"], function($){...});
The downside is performance on pages that require many modules. In this situation you benefit more from having all resources in combined files, but note that query strings will cause the browser not to cache files in any case.. which is also another performance consideration.
Hope that helps
ps. In terms of CSS lazy loading, you could always use javascript to inject link tags into the head adhoc, and provide some javascript interface functions that your other code can call in order to request a CSS dependency dynamically.

Is there any way to run Javascript code before Angular has run?

I'm looking to programmatically add ng-* HTML attributes to various DOM elements. I've had some success with using $compile(obj)($scope); but this secondary compile causes issues with a number of components.
I add the ng-* attributes via jQuery... and yes, I know, directives, but this won't work for me as the ng-* HTML attributes I'm adding are boilerplate actions based on DOM structures. That and directives seem clunky (to say the least) as compared to jQuery DOM manip.
So... is there any way I can add in these boilerplate ng-* HTML attributes BEFORE Angular runs so that I can avoid the re-$compile? What I'd really love is a way to do a pre-init hook on Angular, is there such a beast?
SOLUTION:
#ChrisMartin sent me on the right path to figure out an answer to this question (thanks Chris!). What I ended up doing is this...
First I created a file named "angular-defer-bootstrap.js" that is included before "angular.js" with the following code:
//# Set the window.name to signal Angular to delay bootstrapping until `angular.resumeBootstrap()` is called.
//# See: http://stackoverflow.com/a/21049890/235704 and https://docs.angularjs.org/guide/bootstrap
//# NOTE: This MUST be included BEFORE angular*.js
window.name = 'NG_DEFER_BOOTSTRAP! ' + window.name;
I then created the following function with jQuery to preform any pre-Angular bootstrap code:
//####################
//# Setup the jQuery onDocumentLoad event to handle the pseudo-ng-directive of ng-preinit
//####################
$(document).ready(function () {
var $this, $pre = $('[ng-preinit]');
//# If we have some [ng-preinit]'s to process
if ($pre.length > 0) {
//# Traverse the [ng-preinit] attributes, eval'ing/running each and removing them so Angular doesn't freak out
$pre.each(function() {
$this = $(this);
eval($this.attr('ng-preinit'));
$this.removeAttr('ng-preinit');
});
}
//# Let Angular know it can .resumeBootstrap and remove the flag from window.name
angular.resumeBootstrap();
window.name = window.name.replace('NG_DEFER_BOOTSTRAP! ', '');
});
This is then utilized by including a ng-preinit pseudo-Angular directive/HTML attribute:
<div class="row" ng-controller="IndexController" ng-init="init()" ng-preinit="globalScope.preinit()">
The rub here is that the eval'd code contained within the pseudo-Angular directive ng-preinit has the global scope, rather than the Angular controller's $scope.
With these few lines of code, I can now cleanly hook the "pre-init" (that is, pre-bootstrap) of Angular and do whatever I like without the need to re-$compile (and it's unintended consequences), which is exactly what I wanted!
This is explained in Angular's documentation on manual initialization.
If you need to have more control over the initialization process, you can use a manual bootstrapping method instead. Examples of when you'd need to do this include using script loaders or the need to perform an operation before Angular compiles a page.

Require.js and reusable UI functions

I'm working on a project written using Require.js. There are a number of reused functions that are currently being called from the global scope. These functions involve ui transitions, hide/show, and general on hover events. I want to organize these functions right into require, but not quite sure where/how to include them.
For example let's say in the app there are multiple spots that may call a common function of showDropdown(). And let's say it requires jQuery for the animation. Where or how would be the best place to store the showDropdown function?
Say a simple function like:
function showDropdown(id) {
var thisdropdown = $(id).find('.dropdown');
$(thisdropdown).slideDown();
}
I could create a UI folder, with the different js functions all being their own file. Then simply require them on any other files that are dependent on them. But regardless, those files will need to export their function to the global scope to be accessible correct?
I feel there is an obvious answer/setup as this must be fairly common item.
In addition, I am writing this in a backbone app, but I don't believe that has any direct impact, more of a require.js question.
Create a util library or something like that:
// util.js
define({
showDropdown: function(id) {
var thisdropdown = $(id).find('.dropdown');
thisdropdown.slideDown();
}
});
Then use it elsewhere:
require(['util'], function(util) {
util.showDropdown('my-id');
});

Categories