How to load knockout.observableDictionary plugin in requirejs with shim? - javascript

This is the plugin
https://github.com/jamesfoster/knockout.observableDictionary
Here is a fiddle showing the problem I am experiencing:
https://jsfiddle.net/L4d84nqc/1/
Code:
requirejs.config({
paths: {
'ko': 'https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min',
'ko.observableDictionary' : 'https://rawgithub.com/jamesfoster/knockout.observableDictionary/master/ko.observableDictionary'
},
shim: {
'ko.observableDictionary' : {
deps: ['ko']
}
}
});
require(['ko', 'ko.observableDictionary'], function(ko) {
console.log(ko);
});

I dont think there is a way to add a property via the require registration (could be wrong?). I would simply add the .js file to the bundle, or in the page, and modify the library js like so...
require(["ko"], function(ko){
(function (ko) {
function DictionaryItem(key, value, dictionary) {
.............. all that yummy code
}
})(ko)
});

Related

Google Maps API error, ReferenceError: google is not defined in Backbone.js [duplicate]

I am struggling to load gmaps api with requireJS . This is what I've tried:
requirejs.config({
urlArgs: "noCache=" + (new Date).getTime(),
paths : {
"jquery": "vendor/jquery-1.8.2.min",
"bootstrap": "vendor/bootstrap.min",
"underscore": "libs/underscore-min",
"backbone": "libs/backbone-min",
"template": "libs/template",
"gmaps": "http://maps.google.com/maps/api/js?v=3&sensor=false"
},
shim: {
'backbone': {
deps: ['jquery', 'underscore'],
exports: 'Backbone'
},
'underscore': {
exports: '_'
},
'bootstrap': {
deps: ['jquery']
},
'gmaps': {
deps: ['jquery']
},
'main':{
deps: ['jquery','gmaps']
}
}
});
require(["main"], function (main) {})
But inside main.js when I try to instantiate the geocoder i got ,,undefined is not a function" error.
var geocoder = new google.maps.Geocoder();
Any ideas what could I be doing wrong?
I've managed to sort it out with the async plugin.
A quick example is:
require.config({
paths: {
'async': 'lib/requirejs-plugins/src/async'
}
});
define(['async!http://maps.google.com/maps/api/js?sensor=false'], function() {
// Google Maps API and all its dependencies will be loaded here.
});
Thanks to user1706254 cause official documentation : https://github.com/millermedeiros/requirejs-plugins/ was using the keyword 'define' that wasn't working for me but 'require' is working fine.
I couldn't load directly :
require(["goog!maps,3,other_params:sensor=false"], function(){});
But using the asynchronous way did the trick :
require(['async!http://maps.google.com/maps/api/js?sensor=false'], function(){});
You don't need the async plugin to use Google Maps with require.js. The goal can be achieved using only a simple shim config:
require.config({
paths: {
gmaps: '//maps.googleapis.com/maps/api/js?' // question mark is appended to prevent require.js from adding a .js suffix
},
shim: {
gmaps: {
exports: 'google.maps'
}
}
});
require(['gmaps'], function (gmaps) {
var center = {lat: -34.397, lng: 150.644};
var map = new gmaps.Map(document.getElementById('map'), {
center: center,
zoom: 8
});
new gmaps.Marker({
map: map,
position: center
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.5/require.js"></script>
<div id="map" style="width: 100%; height: 200px"></div>
Following on from hjuster here's a quick example of how to use the async plugin
https://gist.github.com/millermedeiros/882682
There is also goog plugin (requires async and propertyParser), available on github
Usage example for google maps:
require(["goog!maps,3,other_params:sensor=false"], function(){});
#hjuster's answer led me the way and I've solved by a callback function.
define(['async!http://maps.google.com/maps/api/js?key=YOURKEY!callback'],
function (_ExpectedMap) {
callback();
});
Notice the !callback at the end of the url starts with async!, callback method is being called when load operation is done.
function callback()
{
//Now load google maps API dependant libraries
require(['gmapsLib'], function (googlemaps) {
window.GMaps = googlemaps;
}
}
There is another question I lately noticed, another function (onLoad) is in use instead of callback to prevent from timeout error. Interesting.
Couldn't make the plugins work for some reason, but this workaround saved my day:
require(['https://apis.google.com/js/client.js?onload=doNothing'], function() {
// Poll until gapi is ready
function checkGAPI() {
if (gapi && gapi.client) {
self.init();
} else {
setTimeout(checkGAPI, 100);
}
}
checkGAPI();
});
});
Just check if gapi is ready every 100 millisec, until it finally loads.
Found the code in this article http://dailyjs.com/2012/12/06/backbone-tutorial-2/
I guess you can also try it with
if (google && google.maps && google.maps.Geocoder) {
// now google.maps.Geocoder is gonna be defined for sure :)
}

ko is not defined Error in Chutzpah

I'm trying to write Unit Testing for my SPA project. Where we have used the Durandal (Framework), Knockout (Binding) with RequireJs.
I have installed the Chutzpah in Visual Studio 2012.
When i run my Test for the View Model, it throws me below error, even though the knockout js and other js are loaded correctly.
Uncaught ReferenceError: ko is not defined
My Json Config Code:
{
"Framework": "jasmine",
"TestHarnessReferenceMode": "AMD",
"TestHarnessLocationMode": "SettingsFileAdjacent",
"References" : [
{"Path" : "../Scripts/require.js" },
{"Path" : "config.js" }
],
"Tests" : [
{"Path": "tests"}
]
}
My Config Js Code:
require.config({
paths: {
'text': '../Scripts/text',
'durandal': '../Scripts/durandal',
'plugins': '../Scripts/durandal/plugins',
'jquery': '../Scripts/jquery-2.1.4',
'knockout': '../Scripts/knockout-3.3.0'
},
shim: {
}
});
My FirstTest.Js Code:
define(['project/modules/Settings/Subscriber/Viewmodels/Channels'],
function (nChannel) {
describe("Get Channels", function () {
it("will check the Get Channels call and result", function () {
var disp = nChannel.getChannels().then(function () {
var actualResult = ko.toJS(nChannel.Channels);
expect(actualResult.length).toEqual(3);
});
});
});
});
ViewModel Code:
define(['plugins/dialog'], function (dialog) {
var subscriberList = ko.observableArray(); //Getting Error here - while loading the Js for Unit Testing
var JsQ = $; //Getting JQUERY members here. // Works good.
//Other Logics goes here
return {
subscriberList : subscriberList,
JsQ : JsQ
};
});
The Configuration for the Jquery works perfect, since knockout also same as that. But gives error.
Any Idea's / Suggestion why the error?
Do i need to load the ko (knockout) separately?
Edit 1:
I have tried changing the knockout to ko it gives me the error Uncaught Error: Script error for: knockout.
Edit 2:
The problem i'm facing when i apply this solution, those existing code file needs the massive changes and the file counts are in hundreds. From Init.Js we have loaded the Jquery and Knockout. Like below.
requirejs.config({
paths: {
'text': '../Scripts/text',
'durandal': '../Scripts/durandal',
'plugins': '../Scripts/durandal/plugins'
}
});
define('jquery', [], function () {
return jQuery;
});
define('knockout', [], function () {
return ko;
});
So inside any viewmodel we can get the instance of knockout as ko, without declaring the require js stuff in each veiwmodel for the Knockout (As you suggested).
But when i try the same in Chutzpah declaration this is not working. Not sure why.
Hope you understand the problem.
In both modules you show in your question, you use ko but you do not list knockout in your list of dependencies. This is a sure way to get the error you got. Modify your modules to list knockout in the dependencies, and add a corresponding parameter to the callback you give to define. For instance,
define(['knockout', 'plugins/dialog'], function (ko, dialog) {

RequireJS: How do I pass variables from one file to another?

I'm using require with backbone + backbone-forms. I'm currently using RequireJS to seperate code into multiple files. I have models stored in separate files and want to keep form validators separately.
However, I am unable to access variables defined in one files, in another file that depends on this one. What I get is Uncaught ReferenceError: isEmptyName is not defined. isEmptyName is defined in validators and used in model. Any feedback about RequireJS config is also appreciated.
My config:
requirejs.config({
//By default load any module IDs from js/lib
baseUrl: 'js',
paths: {
jquery: 'lib/jquery',
app: 'lib/app',
wizard: 'lib/jquery.bootstrap.wizard.min',
bootstrap: 'lib/bootstrap.min',
underscore: 'lib/underscore-min',
backbone: 'lib/backbone-min',
backboneForms: 'lib/backbone-forms.min',
langSwitcher: 'lib/lang',
cookie: 'lib/cookie',
datepicker: 'lib/bootstrap-datepicker',
mask: 'lib/jquery.maskedinput.min',
validators: 'modules/validators',
// models
personalData: 'models/personal-data',
addressData: 'models/address-data',
workData: 'models/work-data',
productsData: 'models/products-data',
statmentData: 'models/statment-data',
model: 'models/form',
collection: 'collections/form',
view: 'views/form',
setup: 'setup',
send: 'send',
},
shim: {
'underscore': {
deps: ['jquery'],
exports: '_'
},
'backbone': {
deps: ['underscore', 'jquery'],
exports: 'backbone'
},
// all model needs to go within one collection
'bootstrap' : ['jquery'],
'wizard': ['jquery'],
'backboneForms': ['backbone'],
'validators': ['backbone','mask'],
'personalData' : ['backbone','backboneForms','validators'],
'addressData': ['backbone','backboneForms'],
'workData': ['backbone','backboneForms'],
'statmentData': ['backbone','backboneForms'],
//'collection': ['backbone','backboneForms','personalData'],
//'view': ['backbone','backboneForms','personalData']
}
});
Beginning of validators.js
require(['backbone','backboneForms'], function(){
var lettersOnly = /^[A-Za-zęóąśłżźćńĘÓĄŚŁŻŹĆŃ]+$/;
var lettersOnlyDash = /^[A-Za-zęóąśłżźćńĘÓĄŚŁŻŹĆŃ\-]+$/;
var err = {};
var errCh = {};
var errFormat = {};
var isEmptyName = function(value){
err = { message: 'Wpisz imię.'};
if (value.length === 0) return err;
};
Beginning of model.js that needs the validators in validators.js
require(['backbone','backboneForms','mask','validators'], function(backbone,backboneForms,mask,validators){
var PersonalData = Backbone.Model.extend({
schema: {
first_name:{
title: 'Imię',
validators: [isEmptyName, isLetter, minCharCount] //Accessing validators.js members here...
}, ...
I think you're using require when what you really need is define. From When should I use require() and when to use define()?,
With define you register a module in require.js that you than can
depend on in other module definitions or require statements. With
require you "just" load/use a module or javascript file that can be
loaded by require.js.
So here, you have some variables that are defined in one file, but are required to be accessed in another file. Seems like a 'Module', doesn't it? So now, you have two ways of using this file as a module:
Conform to AMD-ness
Conform to chaotic javascript global variable-ness
Using the AMD Approach
validators.js is now a module. Anybody wishing to use 'validator functions' can depend on this module to provide it for them. That is,
define(['backbone','backboneForms'], function(){
var lettersOnly = /^[A-Za-zęóąśłżźćńĘÓĄŚŁŻŹĆŃ]+$/;
var isEmptyName = function(value){
err = { message: 'Wpisz imię.'};
if (value.length === 0) return err;
return {
someVariable: lettersOnly,
someFunction: isEmptyName
}
};
You'll notice that the require has been replaced with define. Now, when somebody (model) depends on validator.js, they can access their dependencies as follows
require(['backbone','backboneForms','mask','validators'],
function(backbone, backboneForms, mask, validators) {
var isEmptyNameReference = validators.someFunction;
...
Using shim
Check Requirejs why and when to use shim config, which references this link which says,
if we were to just add the backbone.js file to our project and list
Backbone as a dependency from one of our modules, it wouldn’t work.
RequireJS will load backbone.js, but nothing in backbone.js registers
itself as a module with RequireJS. RequireJS will throw up its hands
and say something like, “Well, I loaded the file, but I didn’t find
any module in there.”
So, you could have your validator.js populate a global Validator namespace, and still use it the way we used it in the example above.
function(){
var lettersOnly = /^[A-Za-zęóąśłżźćńĘÓĄŚŁŻŹĆŃ]+$/;
var isEmptyName = function(value){
err = { message: 'Wpisz imię.'};
if (value.length === 0) return err;
Globals.Validator = {
someVariable: lettersOnly,
someFunction: isEmptyName
}
}();
Your config.js would then be,
shim: {
'validator': {
deps: ['backbone','backboneForms'],
exports: 'Globals.Validator'
},
...
Note that you can alias the namespace as you wish, but the alias is just a reference to the existing global object/namespace. This is helpful if you have, say, Foo.Bar.Foobar as your namespace, but want to refer to it as FB. Shimming, hence, is a way for non-AMD libraries to adapt to AMD usage. In this case, option 1 should be sufficient.

RequireJS issues while loading and Hamljs

I am using requirejs + jquery + sammy.js + hamljs.
The template engine haml is not working : it runs sammy.haml, but Haml is defined for main.js but not for sammy.haml and cannot run in it. After the page is completely loaded, Haml exists for the console. Why Sammy does not get on well with HAML whereas it should be ?
requirejs.config({
baseUrl: '../../components/',
paths: {
dropkick: 'jquery-dropkick2/jquery.dropkick-1.0.0',
Ladda: 'Ladda/dist/ladda.min',
'jquery-autosize': 'jquery-autosize/jquery.autosize',
mixitup: 'mixitup/jquery.mixitup.min',
jquery: 'jquery/jquery',
sammy: 'sammy/lib/sammy',
'sammy.haml': 'sammy/lib/plugins/sammy.haml',
haml: 'haml/lib/haml',
browser: 'jquery.browser/jquery.browser.min'
},
shim: {
dropkick: {
deps: ['jquery']
},
browser: {
deps: ['jquery']
},
'sammy.haml': {
deps: ['haml']
}
}
});
requirejs(['jquery', 'haml'], function($, Haml) { // They are to be loaded first
requirejs(['sammy', 'sammy.haml', 'browser', 'markdown'], function(sammy, shaml, markdow) { // Then the others
console.log(Haml); // WORK
var app = sammy('#main', function() {
var self = this;
self.use(sammy.Haml);
self.get('/upload', function(context) {
context.app.swap('');
var s = context.render('app/templates/upload.haml', {});
s.appendTo(context.$element())
});
});
});
app.run();
});
});
Finally found the solution ! I defined window.Haml = haml in the main file, and modified sammy.haml.js to get window.Haml function instead of Haml function.

method won't trigger when using requireJS

I have some issue with some requireJS setup. I posted a question before but the scope of the latest changed now.
I have some
requirejs.config({
paths: {
'tmpl': 'vendor/upload/tmpl.min'
}
});
require({
paths: {
'videoupload': 'vendor/upload/jquery.ui.videoupload'
}
}, ['js/main_video.js'], function (App) {
App.initial_video_upload();
});
and finally in main_video.js :
define(['tmpl', 'videoupload'], function () {
function initial_video_upload(tmpl, videoupload) {
'use strict';
$('#videoupload').videoupload({
//...some code
});
}
return{
initial_video_upload: initial_video_upload
}
}
);
This code works perfectly if I don't use requireJS (loading classically each file). In fact, when this code is triggered, I keep on having a message Uncaught TypeError: Object [object Object] has no method 'tmpl', this method is defined in tmpl.min.js. And this method is invoked in vendor/upload/jquery.ui.videoupload, as so
$.widget('videoupload', {
//...
_renderVideo: function (video) {
this._templateElement().tmpl({
id: video.id,
name: video.title
}).appendTo(this._listElement()).find(
this.options['delete-selector']
);
return this;
},
//...
How can I manage that ? (I had earlier an error time out message for this method tmpl, but it disappeared now, so I don't think this is it)
In the configuration object, the path is not the full path to the JS file BUT the path to the directory containing the JS file, so you may want to do something like this in the main_video.js file:
requirejs.config({
paths:{
'upload': 'vendor/upload'
}
});
define(['upload/tmpl','upload/jquery_videoupload'],function(tmpl, videoupload) {
function initial_video_upload(tmpl,videoupload){
'use strict';
$('#videoupload').videoupload({
//...some code
});
}
return{
initial_video_upload: initial_video_upload
}
}
);
And in the main app:
requirejs.config({
paths:{
'js': 'path/to/your/js/folder'
}
});
require(['js/main_video'], function(App) {
App.initial_video_upload();
});
There's a problem in the questions code, so this:
define(['tmpl', 'videoupload'], function () {
should become this:
define(['tmpl', 'videoupload'], function (tmpl, videoupload) {
The first one doesn't expose loaded dependencies to local variables of closure function, so that's might be a problem, although it's not very clear if it's the only one, from the provided code.
I would also like to mention, that it's not a good thing to use multiple requre.js configs, if you're intended to use optimizer. The configs will be overwritten by the last one, so it's a good idea actually to have only one config for the whole project.
Like this:
requirejs.config({
paths: {
'tmpl': 'vendor/upload/tmpl.min',
'videoupload': 'vendor/upload/jquery.ui.videoupload'
}
});

Categories

Resources