I'm trying to use routerjs as a standalone library but can't figure out how to implement the handler lookup as it's said in the documentation, and couldn't find any example.
What I've got so far:
var router = new Router['default']()
router.map(function(match) {
match('/').to('index')
match('/chat').to('chat')
})
I've tried using callbacks in the routes like this, but every callback are called not matter what url I am on so I'm not sure how to use it:
router.map(function(match) {
match('/').to('index', function() {
console.log('always called')
})
match('/chat').to('chat', function() {
console.log('always called as well')
})
})
Has anybody successfully used it?
Related
I want to be able to put the code in one place and call it from several different events.
Currently I have a selector and an event:
$("input[type='checkbox']").on('click', function () {
// code works here //
});
I use the same code elsewhere in the file, however using a different selector.
$(".product_table").on('change', '.edit_quantity', function () {
// code works here //
});
I have tried following the advice given elsewhere on StackOverflow, to simply give my function a name and then call the named function but that is not working for me. The code simply does not run.
$(".product_table").on('change', '.edit_quantity', function () {
calculateTotals() {
// code does not work //
}
});
So, I tried putting the code into it's own function separate from the event and call it inside the event, and that is not working for me as well.
calculateTotals() {
// code does not work //
}
So what am I doing wrong ?
You could pass your function as a variable.
You want to add listeners for events after the DOM has loaded, JQuery helps with $(document).ready(fn); (ref).
To fix your code:
$(document).ready(function() {
$("input[type='checkbox']").on('click', calculateTotalsEvent)
$(".product_table").on('change', '.edit_quantity', calculateTotalsEvent)
});
function calculateTotalsEvent(evt) {
//do something
alert('fired');
}
Update:
Vince asked:
This worked for me - thank you, however one question: you say, "pass your function as a variable" ... I don't see where you are doing this. Can you explain ? tks. – Vince
Response:
In JavaScript you can assign functions to variables.
You probably do this all the time when doing:
function hello() {
//
}
You define window.hello.
You are adding to Global Namespace.
JavaScript window object
This generally leads to ambiguous JavaScript architecture/spaghetti code.
I organise with a Namespace Structure.
A small example of this would be:
app.js
var app = {
controllers: {}
};
You are defining window.app (just a json object) with a key of controllers with a value of an object.
something-ctlr.js
app.controllers.somethingCtlr.eventName = function(evt) {
//evt.preventDefault?
//check origin of evt? switch? throw if no evt? test using instanceof?
alert('hi');
}
You are defining a new key on the previously defined app.controllers.somethingCtlrcalled eventName.
You can invoke the function with ();.
app.controllers.somethingCtlr.eventName();
This will go to the key in the object, and then invoke it.
You can pass the function as a variable like so.
anotherFunction(app.controllers.somethingCtlr.eventName);
You can then invoke it in the function like so
function anotherFunction(someFn) { someFn();}
The javascript files would be structured like so:
+-html
+-stylesheets
+-javascript-+
+-app-+
+-app.js
+-controllers-+
+-something-ctlr.js
Invoke via chrome developer tools with:
app.controllers.somethingCtlr.eventName();
You can pass it as a variable like so:
$(document).ready(function() {
$('button').click(app.controllers.somethingCtlr.eventName);
});
JQuery (ref).
I hope this helps,
Rhys
It looks like you were on the right track but had some incorrect syntax. No need for { } when calling a function. This code should behave properly once you add code inside of the calculateTotals function.
$(".product_table").on('change', '.edit_quantity', function () {
calculateTotals();
});
$("input[type='checkbox']").on('click',function() {
calculateTotals();
});
function calculateTotals() {
//your code...
}
You could just condense it all into a single function. The onchange event works for both the check box and the text input (no need for a click handler). And jQuery allows you to add multiple selectors.
$('input[type=checkbox], .product_table .edit_quantity').on('change', function() {
console.log('do some calculation...');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="product_table">
<input type="checkbox">
<input class="edit_quantity">
</div>
So I've been trying to figure out how front end testing works (unit testing) but I am getting stuck on some point.
So I have my jasmine test set up as follows:
describe('Blabla', function () {
it('returns true', function () {
var people = require(["people"], function(ppl) {
return ppl;
});
expect(people.getTitle()).toBe('People piolmjage');
});
});
But running this gets me:
TypeError: undefined is not a funtion
So obviously, people is undefined. So perhaps my callback comes in too late. But if I remove the callback I get following error:
it('returns true', function () {
var people = require("people");
expect(people.getTitle()).toBe('People piolmjage');
});
Error: Module name "people" has not been loaded yet for context: _. Use require([])
I figure there is something wrong in my setup...Anyone have any idea how to get this FE testing to work?
I did manage to get it to work from console and using define combined with phantomjs and the durandal test files but I need this to work outside of the console and hereby I cannot use this define because the test runner won't find my tests.
That's why I need to use the CommonJS was of getting the required viewmodels.
people model
define([],
function () {
var getTitle = function() {
return "hello";
}
var peopleViewModel = {
title: 'People page',
getTitle: getTitle
};
return peopleViewModel;
});
UPDATE
I got the code working but not with resharper. Following this page from the durandal webpage.
But this gets me console output which is way to unstructured to actually read through.
I can however use the define keyword and then it works fine. So I assume it is the require keyword where I mess up something?
UPDATE 2
So I used fiddler to check what is going on. I also finally got it working (kinda...).
My testfile looks like this now:
///<reference path="../../Scripts/require.js"/>
///<reference path="../../test/lib/jasmine-2.1.3/jasmine.js"/>
///<reference path="../../App/viewmodels/people.js"/>
describe('Blabla', function () {
it('require test', function (done) {
require(['people'], function (people) {
expect(people.title).toBe('People page');
done();
});
});
});
And then I changed my people file:
define("people", ["bla"], function (bla) {
return {
title: 'People page',
bla: bla
};
});
As you can see here, I name my viewmodel to be people.
This works for the testrunner but he doesn't actually get any files through requireJS but only the reference paths. Also this does not fit my needs because the durandal models are unnamed.
Fiddler screenshot:
So basically he does not use requireJS to get the viewmodels and therefor I cannot just use the require.config initializer to get to my viewmodels folder and download every viewmodel using requireJS. Any thoughts?
I finally got it working, took me like a day and a half.
Anyway I don't use resharper anymore, or it's test runner to be more precise.
Chutzpah is the one I turned to in the end. This too took me some research but I got it to the point where it includes everything as I want it to.
Check this post for sure
Here is what I did:
My people.js looks like this:
define(['viewmodels/bla'], function (bla) {
return {
title: 'People page',
bla: bla //testing dependencies on other viewmodels
};
});
Then I also made a bla.js
define(function() {
return {
bla: "bla"
};
});
And now for the tests:
describe('Blabla', function () {
it('require test', function (done) {
require(['viewmodels/people'], function (people) {
expect(people.title).toBe('People page');
done();
});
});
it('dependency on require test', function (done) {
require(['viewmodels/people'], function (people) {
console.log(people.bla);
expect(people.bla.bla).toBe('bla');
done();
});
});
});
And then eventually, reading the answers on the link provided on top I had to create a Chutzpah config file to create a test harnass:
{
"Framework": "jasmine",
"TestHarnessReferenceMode": "AMD",
"TestHarnessLocationMode": "SettingsFileAdjacent",
"References" : [
{"Path" : "../Scripts/require.js" },
{"Path" : "requireConfig.js" }
],
"Tests" : [
{"Path": "specs"}
]
}
Now, running the tests with Visual studio test runner actually gets me everything I need and as you can see, I can now access all my viewmodels through require like so: require(['viewmodels/whateverviewmodel'], function(whateverviewmodel){....})
I hope this answer can get people on their way to testing your (Durandal)SPA using Jasmine and RequireJS.
I know my viewmodels in this answer, nor in the question itself, say much but this should get you an idea of how to go about all of this.
Small Edit
You can now also skip the callback mess with require([]... inside of the tests and build your tests like you do your viewmodels with define
define(['viewmodels/people'], function (people) {
describe('Blabla', function () {
it('require test', function () {
expect(people.title).toBe('People page');
});
it('dependency on require test', function () {
console.log(people.bla);
expect(people.bla.bla).toBe('bla');
});
});
});
This gets you less indents and is more readable in itself.
The require call provided by RequireJS is inherently asynchronous so you need to do something like this:
it('returns true', function (done) {
require(["people"], function(people) {
expect(people.getTitle()).toBe('People piolmjage');
done(); // Signal that the test is done.
});
});
The first attempt you show in your question cannot work. That's the classical "trying to return values synchronously form asynchronous code" mistake. The second attempt with require("people") does not work either because this require call is pseudo-synchronous and will work only if the module requested is already loaded. See this answer for an explanation of how this pseudo-synchronous require works.
In Polymer and Backbone.js I was digging through the source to find out how they organize their API's for the user.
Currently I'm using a revealing pattern and runs NAMESPACE.init(args) but I liked the way those two libs are making you set everything in the ready or attached methods as callback functions but I don't know the name to do some more research on it with.
Example: I call Polymer and assign something into its ready method as a callback function, I assume it's just overwriting an internal ready method and it stores it for later.
Backbone looks mostly the same
Polymer({
ready: function() {
console.log("im ready like!")
}
});
Perfect I guesss..
using underscore.js extend function rather then jquerys in this example but I guess jquery would work all the same.
Tacos = function (args) {
this.commands = {
popcorn : function(){
console.log("pop'n fun!");
},
render : function(){
vars.mine = args.cat;
}
}
// using underscore.js _.extend function
// this merges the commands and replaces the popcorn function with
// the users function! Fun!
_.extend(this.commands, args);
}
var u = new Tacos({
popcorn : function(){
console.log("popcorn denied a tron! ");
}
});
u.commands.popcorn()// should return "popcorn denied a tron!"
I generally access my Parse app by going to www.mysite.com.
However, I want to be able to do something like www.mysite.com/code or www.mysite.com?page=code, and for this to show a specific view within the app, without having to click through. In other words, I want to detect that a GET variable is present, and use it to determine which view to show.
Can anyone point me in the right direction?
You can use Backbone's Router. There's an example in the Anypic.org source code you can use.
var AppRouter = Backbone.Router.extend({
routes: {
"pic/:object_id": "getPic",
"*actions": "defaultRoute"
},
getPic: function(object_id) {
App.showLandingPage();
var query = new Parse.Query(Photo);
query.include("user");
query.get(object_id, {
success: function(photo) {
App.landingPageView.showPhoto(photo);
},
error: function(object, error) {
console.error(error);
// The object was not retrieved successfully.
// error is a Parse.Error with an error code and description.
App.landingPageView.showError();
}
});
},
defaultRoute: function(actions) {
App.showHomePage();
}
});
In this example, navigating to https://anypic.org/pic/XXXXX will call getPic and pass along the value of XXXXX to the getPic function.
I don't have much familiarity with parse.com, but if you just want to access GET variables within javascript, it's not terribly difficult:
http://www.onlineaspect.com/2009/06/10/reading-get-variables-with-javascript/
I am writing a small module which will have several different aspects to it, all based around ajax calls. I want to allow the main ajax functions i.e beforeSend, success, complete etc (I am using jQuery) to be customizeable.
What is the best way to do this?
I currently have an options object in the module which can be extended with an options object passed to an init function. In here, I am passing a 'callbacks' object with nested ojects for each different type of action as in...
var insertCallbacks = {
before : function() {
},
success : function() {
},
error : function() {
},
complete : function() {
}
};
var updateCallbacks = {
before : function() {
},
success : function() {
},
error : function() {
},
complete : function() {
}
};
var callbacks = {
insert : addCallbacks,
update : removeCallbacks
};
MY_MODULE.init( {callbacks : callbacks} );
The problem is this then becomes a bit messy, testing for the existence of each of these methods on the ajax callbacks.
Can anyone offer any advice on a good/better pattern for this.
I would go with custom events rather than callbacks. So in your module you will have code like:
MY_MODULE.trigger('updateComplete');
and in all parts outside of module (as well as inside if needed), you bind handlers (now they are callbacks):
MY_MODULE.bind('updateComplete', function() {
alert('update completed');
} );
Custom events in jQuery open doors to complex behaviors, or google any other article. Custom events will help you to keep code structured, and easier to test
ADD ON: with callbacks you need always to check if there is any, so you code becomes
if ( callbacks && callbacks.insert ) {
callbacks.insert();
}
improving your module functionality, and enhancing it, one day you get a situation that few callbacks should be passed for the same situation (e.g. two entities or UI components are interested in module 'updating')... It will make your job too difficult. With events you always have one code line
MY_MODULE.trigger('updateComplete');
with no conditions to check if there is any handler attached (interested in an event), and you can have as many as needed handlers for the same event.
Just do the same thing jQuery does. Let your module fire events (via trigger()) and attach handlers to these events when necessary (via bind()). This way you do not have to check inside your module what functions are listening (or if at all).
Since your code does not contain anything I could use for a sample, it's kind of hard to add code to this answer.