require.js dependencies - how to incorporate dependency methods in additional script - backbone.js

I'm using raphael.js in a backbone.js project. Importing raphael works like a charm (this version is AMD compliant). Everything is working as expected. "app" here is a predefined global object defined in another app.js file.
define(['jquery', 'underscore', 'app', 'backbone','raphael' ],
function($, _, app, Backbone, Raphael) {
var AppView = Backbone.View.extend({
initialize: function() {
app.paper = Raphael(0, 0, app.w, app.h);
}
...
}) })
Now my app.paper has all Raphael methods. Awesome!
I just discovered in the Raphael API that I can add my own predefined methods using Raphael.el and Raphael.fn
initialize: function() {
app.paper = Raphael(0, 0, app.w, app.h);
Raphael.el.myfill = function(){
this.attr('fill', '90-#fff-#000');
}
app.paper.circle(x,y,r).myfill(); //it works! (Brilliant!)
}
}
My question is, how can I put the Raphael.el.myfill definition along with other Raphael.fn.mydefinedmethods into another javascript file and bring it into the AppView above?
I don't want to clog up my AppView file with lengthy definitions, and I also would like to provide variability as to which Raphael.[el|fn] definitions I use in different views. But since these object constructors are already part of the Raphael.js object that I've already pulled in as a dependency, I'm not sure how to separate the Raphael.el and Raphael.fn definitions out using the require.js protocol. Before require.js I would have simply put such definitions in another myRaphaelDefs.js file, added another "script" tag to my html and they'd all be available always, but this is the 2015 and I've jumped on the modular js bandwagon.
I'm using RedRaphael branch which is AMD compliant, so I have no "define" wrapper on the Raphael.js itself. If this library did come with such a wrapper I might try adding the outsourced definitions directly into the Raphael.js as dependencies. (not an option) RedRaphael works with require.js right out of the box, so there's no "define" wrapper there.

What we do is add a wrapper around our libraries and route it with the map option in require.config, so the wrapper gets the original library, but everything else goes throw the wrapper:
raphael-wrapper.js:
define(['raphael'], function (Raphael) {
Raphael.el.myfill = function(){
this.attr('fill', '90-#fff-#000');
};
return Raphael;
});
require.config:
{
paths: {
'raphael': '/path/to/rahpael'
},
map: {
'*': {
'raphael': '/path/to/rahpael-wrapper'
},
'/path/to/rahpael-wrapper': {
'raphael': 'raphael'
}
}
}

Related

Is there a tool to split Protractor tests into component files?

I'm working on building an extensible automated test suite with Protractor (angularJS/Jasmine framework).
As long as all of my variables and functions and jasmine are in the same file, it runs semi-okay.
But every effort I make to break it into export/require is a nightmare.
Is there a tool that will just find the parts of my test and automatically reformat it and break it into individual files and folders, so that the thing will actually run?
Thanks!
I don´t know a tool for what you want. However if I were you, I would keep working with node's way of sharing files (export/require). Once you understand it, if you keep it clean and tidy, you could grow your app in a "clean" way.
EDIT:
As #MBielski said, Page Objects Model is also helpful when maintaining you test code.
Definition from the Selenium team:
Page Object is a Design Pattern which has become popular in test
automation for enhancing test maintenance and reducing code
duplication. A page object is an object-oriented class that serves as
an interface to a page of your AUT. The tests then use the methods of
this page object class whenever they need to interact with that page
of the UI. The benefit is that if the UI changes for the page, the
tests themselves don’t need to change, only the code within the page
object needs to change. Subsequently all changes to support that new
UI are located in one place.
And now an example without using page objects and then one using it.
Without:
describe('angularjs homepage', function() {
it('should greet the named user', function() {
browser.get('http://www.angularjs.org');
element(by.model('yourName')).sendKeys('Julie');
var greeting = element(by.binding('yourName'));
expect(greeting.getText()).toEqual('Hello Julie!');
});
});
With:
var AngularHomepage = function() {
var nameInput = element(by.model('yourName'));
var greeting = element(by.binding('yourName'));
this.get = function() {
browser.get('http://www.angularjs.org');
};
this.setName = function(name) {
nameInput.sendKeys(name);
};
this.getGreeting = function() {
return greeting.getText();
};
};
describe('angularjs homepage', function() {
it('should greet the named user', function() {
var angularHomepage = new AngularHomepage();
angularHomepage.get();
angularHomepage.setName('Julie');
expect(angularHomepage.getGreeting()).toEqual('Hello Julie!');
});
});
You can also define various test suites. Take a look at this config file:
exports.config = {
seleniumAddress: 'http://localhost:4444/wd/hub',
capabilities: {
'browserName': 'chrome'
},
// Spec patterns are relative to the location of the spec file. They may
// include glob patterns.
suites: {
homepage: 'tests/e2e/homepage/**/*Spec.js',
search: ['tests/e2e/contact_search/**/*Spec.js',
'tests/e2e/venue_search/**/*Spec.js']
},
jasmineNodeOpts: {
showColors: true, // Use colors in the command line report.
}
};

Singleton model in Backbone multipage app with RequireJS

I have a Backbone multipage app written with the use of RequireJS. Since it's multipage I decided not to use a router as it got too messy. I've tried multiple ways of creating a singleton object to be used throughout the app
var singletonModel= Backbone.Model.extend({
}),
return new singletonModel;
For the above I'm just referencing the singletonModel model in my class using the define method and then calling it as is
this.singleton = singletonModel;
this.singleton.set({'test': 'test'});
On a module on my next page when I then call something similar to
this.singleton = singletonModel;
var test = this.singleton.get('test');
The singleton object seems to get re-initialized and the test object is null
var singletonModel= Backbone.Model.extend({
}, {
singleton: null,
getSingletonModelInst: function () {
singletonModel.singleton =
singletonModel.singleton || new singletonModel;
return singletonModel.singleton;
}
});
return singletonModel;
For the above I'm just referencing the singletonModel model in my class using the define method and then calling it as is
this.singleton = singletonModel.getSingletonModelInst();
this.singleton.set({'test': 'test'});
On a module on my next page when I then call something similar to
this.singleton = singletonModel.getSingletonModelInst();
var test = this.singleton.get('test');
Again it looks like the singleton object is getting re-initialized and the test object is null.
I'm wondering if the issue is because I'm using a multi-page app with no router so state is not been preserved? Has anyone tried using a singleton object in a multi-page app before? If so did you do anything different to how it's implemented on a single-page app?
Thanks,
Derm
Bart's answer is very good, but what it's not saying is how to create a singleton using require.js. The answer is short, simply return an object already instanciated :
define([], function() {
var singleton = function() {
// will be called only once
}
return new singleton()
})
Here we don't have a singleton anymore :
define([], function() {
var klass = function() {
// will be called every time the module is required
}
return klass
})
It's may sound a little ... but, you doing a multi-page application, so when you move to next page, a whole new document was loaded into the browser, and every javascript on it will be loaded too, include require.js and your model. so the require.js was reloaded, and it create your model again, so you got a different model than you thought.
If above was true, my opinion is your model will "live" on a single page, when you jump to then next page, that model was "kill"ed by browser. so If you want see it again, store it on somewhere else, maybe server or localstroe, on the former page. and in the next page load it back from server or localstore, and wrap it into a Backbone model, make it "live" again.
Here is how I implemented a singleton in a recent Backbone/Require application. State is remembered across any number of views.
instances/repoModel.js
define(['models/Repo'],
function(RepoModel){
var repoModel = new RepoModel();
return repoModel;
}
);
models/Repo.js
define(['backbone'],
function(Backbone){
return Backbone.Model.extend({
idAttribute: 'repo_id'
});
}
);
views/SomePage.js
define(['backbone', 'instances/repoModel'],
function(Backbone, repoModel) {
return Backbone.View.extend({
initialize: function() {
repoModel.set('name', 'New Name');
}
});
}
);

Require.js module not seeing Backbone Router.js

In this simple Require/Backbone app
https://github.com/thisishardcoded/require-prob
Why does app.js see Router but TestView.js not?
Here is the first line of app.js
define(['router'],function (Router) {
and here is the first line of TestView.js
define(['backbone','router'],function(Backbone,Router){
Check out the repo for full details, download, run and check console log if you feel so inclined
Thanks!
Jim
More: Ok, the answer is - because of the order in which it is loaded and even if that were altered I have a circular dependency don't I? TestView needs Router, Router needs TestView.
In which case the solution might be
var r=require('router);
r.navigate or whatever
but, that seems like a shame that Router is not directly accessible everywhere and, is the above method good practice anyway?
Surely it happens because of circular dependency. To resolve it, you either pass router to view's constructor and remove router dependency from view's module, or use require('router') in your view.
1st option, router.js:
app_router.on('route:test', function () {
var testView = new TestView({router: app_router});
testView.render();
})
1st option, view.js:
define(['backbone'], function(Backbone){
var TestView=Backbone.View.extend({
el: '#test',
initialize: function() {
// get router from constructior options
this.router = this.options.router
},
render: function(){
this.$el.html('<p>Foo!</p>');
console.log("TestView.js does not find 'Router',", this.router);
}
});
return TestView;
});
2nd option, view.js:
define(['backbone','router'], function(Backbone, router){
// at this point router module is not loaded yet so router is undefined
var TestView=Backbone.View.extend({
el: '#test',
initialize: function() {
// at this point router module is loaded and you may access it with require call
this.router = require('router');
},
render: function(){
this.$el.html('<p>Foo!</p>');
console.log("TestView.js does not find 'Router',", this.router);
}
});
return TestView;
});
2nd option is also described here: http://requirejs.org/docs/api.html#circular
You should define baseUrl property in your main.js file that contains RequireJS config.
In this way all paths to modules in your application will be relative to that baseUrl.
See:
http://requirejs.org/docs/api.html#jsfiles
http://requirejs.org/docs/api.html#config-baseUrl
I downloaded and inspected your code. Following could be the issues:
require.js only works with AMDs. Since backbone no longer supports AMD. You will need to use AMD enabled version of Backbone. You can get it here
TestView is the dependency in you Router. So it loads before the Router is loaded.
You might want to improve the coding pattern. Here is my suggestion:
App.js
define([
'backbone',
'router',
], function(Backbone, MainRouter){
'use strict';
var AppView = Backbone.View.extend({
initialize: function(){
App.router = new MainRouter();
Backbone.history.start();
}
});
return AppView;
});
Router.js
define([
'backbone',
'view/TestView'
], function(Backbone, TestView){
var Main = Backbone.Router.extend({
routes: {
'test': 'test'
},
test: function(){
new TestView({
// pass model or collection to the view
// model: new TestModel // remember to require
});
}
});
return Main;
});
EDIT
Listening to events:
// in main.js
var window.Vent = {};
_.extend(window.Vent, Backbone.Events);
// now in any view you can trigger a event
$('something').on('click', function(){
window.Vent.trigger('somethinghappened', this);
// this is reference to current object
});
// now in other view you can do
window.Vent.on('somethinghappened', this.run, this);
// this in the end is the reference we passed when event was triggered
run: function(obj){
//this function will run when the event is triggered
// obj is the object who triggered the event
}
PS: why do you want to use router in view?? I have built quite a few backbone apps. Never needed to do so.
You can use available Backbone.history.navigate to achieve your goal easier, because Router.navigate is a simple wrapper for it. Consider this part of Backbone source:
navigate: function(fragment, options) {
Backbone.history.navigate(fragment, options);
return this;
},

Backbone EventAggregator issue and questions

I am building a Backbone app using require.js for modular loading and Marionette to help with my application structuring and functionality. I have set up a require module for the event aggregator like this:-
define(['backbone', 'marionette'],function(Backbone, Marionette){
var ea = new Backbone.Wreqr.EventAggregator();
ea.on('all', function (e) { console.log("[EventAggregator] event: "+e);});
return ea;
});
I was hoping to pass it into my other require modules and have it function as a central event handling and messaging component and I am getting some success with this. I can pass the vent as a dependency into other modules without problem like so:-
define(['marionette', 'text!templates/nav.html', 'shell/vent'], function (Marionette, text, vent) {
return SplashView = Marionette.ItemView.extend({
template : text,
events : {
'click #splashContinueButton': 'onButtonClick'
},
onButtonClick : function(evt) {
vent.trigger('onSplashContinueClick');
}
});
});
The problem I am having is that although all the events are getting triggered across the different places in my app (which I can see in the console log), I am not able to listen to them in some parts of my app. For instance I have a Marionette module (loaded at runtime as a require module) which is trying to pick up some events like this:-
var SplashModule = shellApp.module("SplashModule");
SplashModule.addInitializer(function(){
vent.on("onSplashModelLoaded", this.onSplashModelLoaded);
vent.on("onSplashContinueClick", this.onSplashContinueClick);
}
I get nothing, even though if I log the vent from this place I can see it as an object. In the log, it contains an array of events that actually only contain the events being listened to by the root level application, not any other events that other parts of the app are listening for. And this is where my understanding falls apart: I thought I could use the event aggregator as a global communication and messaging system across my application structure. Can anyone please shed any insight into what might be going on?
Much thanks,
Sam
* UPDATE/EDIT/SOLUTION *
Hello, well, I have it working now (only 5 minutes after posting the above - doh!). Basically, adding my listeners in the initializer event of the module was too early (as far as I can tell). I moved them further along the chain of functions and now everything is behaving as expected.
The change I had to make to get it working was that I had to remove the vent listener "onSplashContinueClick" within the module further along. Before this change, it was in the initializer function but now it is further along:-
define(["backbone", "marionette", "shell/vent", "shell/shellapp", "shell/splash/splashmodel", "shell/splash/splashview"], function(Backbone, Marionette, vent, shellApp, SplashModel, SplashView){
var SplashModule = shellApp.module("SplashModule");
SplashModule.addInitializer(function(){
trace("SplashModule.addInitializer()");
SplashModule.model = SplashModel;
SplashModule.model.fetch({
success:function(){
//trace("splashModel.fetch success")
SplashModule.onSplashModelLoaded();
},
error:function() {
//trace("splashModel.fetch error")
}
});
});
SplashModule.addFinalizer(function(){
});
SplashModule.initView = function () {
//trace("SplashModule.initView()");
SplashModule.mainView = new SplashView({model: SplashModel});
shellApp.mainRegion.show(SplashModule.mainView);
vent.on("onSplashContinueClick", this.onSplashContinueClick);
};
SplashModule.end = function () {
trace("SplashModule.end()");
shellApp.mainRegion.close();
vent.trigger("onSplashModuleComplete");
};
// events
SplashModule.onSplashModelLoaded = function () {
trace("SplashModule.onSplashModelLoaded");
SplashModule.initView();
};
SplashModule.onSplashContinueClick = function () {
trace("SplashModule.onSplashContinueClick()");
SplashModule.end();
};
return SplashModule;
});
I am guessing the problem has to do with the order of when dependencies are available and/or ready. I believe the vent was not ready for the listener during the initializer method. This may well be tied up to my usage of Marionette modules within require modules.
Using RequireJS also involves some clean modules...
Backbone.Wreqr.EventAggregator is a module that is part of Marionette.js (for the record, Derrick Bailey just put this module that is made by someone else inside his library, same thing for Backbone.BabySitter)
using RequireJS, you are limited to see what is exported by the library, and in this case Marionette
I think the best way is to split marionette into the 3 modules it actually contains backbone.babysitter, backbone.wreqr, and marionette.
Then you have to create a shim for each module
I used this
require.config({
baseUrl: "/Scripts/",
paths: {
"json2": "vendor/JSON2",
"backbone": "vendor/backbone/backbone.1.1.0",
"localStorage": "vendor/backbone/backbone.localStorage.1.1.9",
"marionette": "vendor/backbone/backbone.marionette.1.8.6",
"bootstrap": "vendor/bootstrap/bootstrap.3.1.1",
"jquery": "vendor/jquery/jquery.1.8.3",
"text": "vendor/Require/text.0.27.0",
"underscore": "vendor/underscore/underscore.1.5.2",
"wreqr": "vendor/backbone/backbone.wreqr",
"babysitter": "vendor/backbone/backbone.babysitter",
},
shim: {
"json2": {
exports: "JSON"
},
"jquery": {
exports: "$"
},
"underscore": {
exports: "_"
},
"bootstrap": {
deps: ["jquery"]
},
"backbone": {
deps: ["underscore", "jquery"],
exports: "Backbone"
},
"validation": {
deps: ["backbone"],
exports: "Backbone.Validation"
},
"wreqr": {
deps: ["backbone", "underscore"],
exports: "Backbone.Wreqr"
},
"marionette": {
deps: ["backbone", "babysitter", "wreqr"],
exports: "Backbone.Marionette"
},
"localStorage": {
deps: ["backbone"],
exports: "Backbone.LocalStorage"
}
}
});
once you have this, you will be able to use wreqr
There's another trick in your script, the fact you write
define(['backbone', 'marionette'],function(Backbone, Marionette){
is a bit disturbing, because you will never know if the use of Backbone or Marionette in your implementation is made on purpose or not. I mean, the namespaces related to backbone and marionette are Backbone and Marionette; I suggest you alias Backbone as backbone and Marionette as marionette like this:
define(['backbone', 'marionette'],function(backbone, marionette){. Doing such , you will be able to check if your module has been downloaded on demand by RequireJS or not.
Then once the shim has been created, your first block code should look like this
define(["wreqr"],function(wreqr){
var ea = new wreqr.EventAggregator();
ea.on('all', function (e) { console.log("[EventAggregator] event: "+e);});
return ea;
});

Understanding Backbone architecture base concepts

I'm trying to working with backbone but I'm missing it's base concepts because this is the first JavaScript MVVM Framework I try.
I've taken a look to some guide but I think I still missing how it should be used.
I'll show my app to get some direction:
// Search.js
var Search = {
Models: {},
Collections: {},
Views: {},
Templates:{}
};
Search.Models.Product = Backbone.Model.extend({
defaults: search.product.defaults || {},
toUrl:function (url) {
// an example method
return url.replace(" ", "-").toLowerCase();
},
initialize:function () {
console.log("initialize Search.Models.Product");
}
});
Search.Views.Product = Backbone.View.extend({
initialize:function () {
console.log("initialize Search.Views.Product");
},
render:function (response) {
console.log("render Search.Views.Product");
console.log(this.model.toJSON());
// do default behavior here
}
});
Search.Models.Manufacturer = Backbone.Model.etc...
Search.Views.Manufacturer = Backbone.View.etc...
then in my web application view:
<head>
<script src="js/jquery.min.js"></script>
<script src="js/underscore.min.js"></script>
<script src="js/backbone/backbone.min.js"></script>
<script src="js/backbone/Search.js"></script>
</head>
<body>
<script>
var search = {};
search.product = {};
search.product.defaults = {
id:0,
container:"#search-results",
type:"product",
text:"<?php echo __('No result');?>",
image:"<?php echo $this->webroot;?>files/product/default.png"
};
$(function(){
var ProductModel = new Search.Models.Product();
var ProductView = new Search.Views.Product({
model:ProductModel,
template:$("#results-product-template"),
render:function (response) {
// do specific view behavior here if needed
console.log('render ProductView override Search.Views.Product');
}
});
function onServerResponse (ajax_data) {
// let's assume there is some callback set for onServerResponse method
ProductView.render(ajax_data);
}
});
</script>
</body>
I think I missing how Backbone new instances are intended to be used for, I thought with Backbone Search.js I should build the base app like Search.Views.Product and extend it in the view due to the situation with ProductView.
So in my example, with render method, use it with a default behavior in the Search.js and with specific behavior in my html view.
After some try, it seems ProductModel and ProductView are just instances and you have to do all the code in the Search.js without creating specific behaviors.
I understand doing it in this way make everything easiest to be kept up to date, but what if I use this app in different views and relative places?
I'm sure I'm missing the way it should be used.
In this guides there is no code used inside the html view, so should I write all the code in the app without insert specific situations?
If not, how I should write the code for specific situations of the html view?
Is it permitted to override methods of my Backbone application?
Basically, you should think of the different parts like this:
templates indicate what should be displayed and where. They are writtent in HTML
views dictate how the display should react to changes in the environment (user clicks, data changing). They are written in javascript
models and collections hold the data and make it easier to work with. For example, if a model is displayed in a view, you can tell the view to refresh when the model's data changes
then, you have javascript code that will create new instances of views with the proper model/collection and display them in the browser
I'm writing a book on Marionette.js, which is a framework to make working with Backbone easier. The first chapters are available in a free sample, and explain the above points in more detail: http://samples.leanpub.com/marionette-gentle-introduction-sample.pdf

Resources