How do I Implement router in CanJS - javascript

I am taking help of the https://github.com/thinkadoo/Projects application. I have built a similar app with the help of this one. My application is using d3 charts instead of the one this uses. My app initializes the routers as
var patientStatus = new PatientStatus('#application', {'credentials':Credentials,'secret':Secret});
Now if i want to implement Router then what changes should be done?
Here is my JSFiddle with both implementations. The first one is working. But the later part where in I am initializing the Router doesnt seem to work.
http://jsfiddle.net/sweety1112/YMAjm/
Can some one help me.

Here is an updated Fiddle that shows how routing works:
var Router = can.Control({
defaults: {}
}, {
init: function() {
// this.element.html(can.view('#index', {}));
},
':type/:id route': function(data) {
console.log('Type:', data.type);
console.log('Id:', data.id);
}
});
can.route.ready(false);
new Router('#content');
can.route.ready(true);
Basically, what you do is initialize your named placeholders and tell the controller that this should be handled by the route processor. Now if you go to a URL like #!test/23 the data of the handler will contain a type and id property.

Related

Assertion Failed: You must use Ember.set() to set the property (of [object Object]) to `[object Object]`

I am trying to integrate a Javascript library I'm building with EmberJS.
Example almost working integration:
https://github.com/pubnub/open-chat-framework/blob/ember/examples/ember
The library returns a single object with many nested objects. The library is based on network events, so the child objects are updated periodically without user input. The updated fire events that can be listened to.
This causes problems for EmberJS, because Ember requires every property update to be done via Ember.set() which my library does not use.
The library is a general purpose JS library so I am refusing to add Ember specific code to it. I am wondering how to solve the above error without rewriting my library.
How can I wrap an event based library in a way Ember would like? I have previously tried globals and an Ember service.
In other examples I have seen people wrap every method of the library in Ember specific code. This seems repetitive.
Is it possible to manually tell Ember of changes to the root object and have Ember ignore all other changes? Meaning, can I have ember NOT observe changes and manually tell ember when things change?
The library includes a root event emitter that is notified of all changes to any object within the tree.
ember-cli: 2.11.1
node: 6.7.0
os: darwin x64
You don't need to change the awesome lib you created.
Everybody wants to use it, should use it in the way of ember.
Just need to change controller of example like this :
import Ember from 'ember';
export default Ember.Controller.extend({
OCF: null,
me: null,
messages: [],
messageInput: '',
init: function() {
this._super(...arguments);
// test
let OCF = window.OpenChatFramework.create({
rltm: {
service: 'pubnub',
config: {
publishKey: 'pub-c-07824b7a-6637-4e6d-91b4-7f0505d3de3f',
subscribeKey: 'sub-c-43b48ad6-d453-11e6-bd29-0619f8945a4f',
restore: false
}
},
globalChannel: 'ocf-demo-ember-2'
});
this.set('OCF', OCF);
// create a user for myself and store as ```me```
let me = this.get('OCF').connect(new Date().getTime());
this.set('me', me);
this.get('me').plugin(window.OpenChatFramework.plugin.randomUsername(this.get('OCF').globalChat));
this.get('OCF').globalChat.on('message', (payload) => {
console.log(payload)
this.get('messages').pushObject(payload);
});
},
actions: {
sendChat: function() {
let messageInput = this.get('messageInput');
if(messageInput) {
this.get('OCF').globalChat.send('message', {
text: messageInput
});
Ember.set(this, 'messageInput', '');
}
return false;
}
}
});

Meteor subscribe on event/render

is there a way to subscribe in meteor when an event triggers or a template is rendered? Im trying to get a message popup and subscribe to all usernames when this happens.
Tried:
Template.newMessage.rendered = function(){
Meteor.subscribe("allUsernames");
}
And:
Template.layout.events({
"click #new-message": function(e, t){
$("#styledModal").modal();
Meteor.subscribe("allUsernames");
}
});
Neither work though, any way to do this or do I have to use a different route? Im using iron router
Im trying to understand why you would wanna do subscribe is such a late state of the process?
I would recommand that u subscribe in the waitOn property in IronRouter.
If you use the waitOn property to subscribe to all users, within that route, you can just display them in the popup =)
1 of the 7 Meteor principles is:
Latency Compensation. On the client, use prefetching and model simulation to make it look like you have a zero-latency connection to the database.
source:
http://docs.meteor.com/#sevenprinciples
Hope this helps,
Alex
You could try to do it using Deps and Session (although your code should work too, maybe you have a problem with publications?).
In your main template rendered function put this:
Template.layout.rendered = function() {
Session.set('getAllUsers',false);
Deps.autorun(function () {
if(Session.get('getAllUsers' == true)
Meteor.subscribe('allUsernames');
})
}
Then in the template that opens the user list:
Template.newMessage.rendered = function(){
Session.set('getAllUsers',true); //this should trigger Deps.autorun and subscribe.
}

Marionette - Relationships between Application and Module

We are currently building a Marionette based application.
Basically, we have a Marionette Application that has multiple regions defined on it.
Each region will act as a container for different Modules to display their views. I want each Module to have full control of what is being displayed in it's container, but I want the Application to allocate these regions. For simplicity, let's say that each module just has a simple ItemView.
I'm considering 2 approaches to populating those regions with the module views.
The first approach says that when each module is initialized, it will create its view and it will call the application to display its view in the specified region, for example:
var app = new Marionette.Application();
app.addRegions({
regionA: "#regionA",
regionB: "#regionB"
});
app.module("moduleA", function(moduleA, app, ...){
moduleA.on("start", function(){
var viewA = new MyViewA();
app.regionA.show(viewA);
}
});
app.module("moduleB", function(moduleB, app, ...){
moduleB.on("start", function(){
var viewB = new MyViewB();
app.regionB.show(viewB);
}
});
The second approach says that each module should expose some function that returns its view. The Application will call that function when ready and it will stick the view in the designated region.
I'm not sure which approach is better and would be happy to hear opinions.
I would definitely go with the second approach, after having gone with the first approach in the past I am now hitting the limitations of this approach and moving to the second approach. I wrote a blog post about it here
It depends which approach you take, both are fine, we choose the second option because we use require.js to load our modules dynamically.
var dashboardPage = Backbone.Marionette.Layout.extend({
template: Handlebars.compile(tmpl),
regions: {
graphWidget : "#graphWidget",
datePickerWidget: "#datePickerWidget",
searchWidget : "#searchWidget"
},
widget: {
graphWidget: null,
datePickerWidget: null,
searchWidget: null,
},
initialize: function(options){
this.someId= options.someId;
//if have someId ID - fetch model;
if(this.someId){
//fetch model if campaignId not null
this.modelAjax = this.model.fetch();
}
onShow: function() {
var that = this;
if(this.modelAjax){
this.modelAjax.done(function(){
that.widget.graphWidget= new graphWidget(graphWidgetOptions);
that.listenTo(that.widget.graphWidget, 'graphWidget', that.getGraphWidgetData, that);
....
that.graphWidget.show(that.widget.graphWidget);
that.datePickerWidget.show(that.widget.datePickerWidget);

How do I create multi-page applications with Meteor?

I am new to Javascript and just started fiddling around with Meteor out of curiosity. What really surprises me, is that it seems that all HTML content gets combined into a single page.
I suspect there is a way to introduce some handling of URLs directing to special pages. It seems that the "todo" example is capable of doing this via some kind of Router class. Is that the "canonical" way of URL handling?
Assuming I can handle URLs, how would I structure my HTML code to display separate pages? In my case they could each have completely separate sets of data, so no HTML code needs to be shared at all.
Jon Gold's answer used to be correct, but as of Meteor 0.5.4:
Work has now shifted to Iron Router. Please consider using IR instead of Router on new projects!
Thus, the current "canonical" way to do this is probably to use IronRouter.
As far as I am aware, there is currently no out of the box way to do this.
What I suggest to do, is to use Backbone.js smart package.
Backbone.js comes with the push-state Router, and if the user's browser doesn't support that it will fallback to hash urls.
In your meteor app directory type this meteor add backbone.
Then somewhere in your client-side code create a Backbone.js Router like so:
var Router = Backbone.Router.extend({
routes: {
"": "main", //this will be http://your_domain/
"help": "help" // http://your_domain/help
},
main: function() {
// Your homepage code
// for example: Session.set('currentPage', 'homePage');
},
help: function() {
// Help page
}
});
var app = new Router;
Meteor.startup(function () {
Backbone.history.start({pushState: true});
});
Then somewhere in your Handlebars template, you can create a helper that will render a page based on the value set in Session's "currentPage".
You can find more information about backbone.js router here: http://backbonejs.org/#Router
Also relevant information on how to create a Handlebars helper method in Metoer here: http://docs.meteor.com/#templates
Hope this helps.
Meteor-Router makes this really easy. I've been using it in some apps I've been building with Telescope as a good reference. Have a look at Telescope's router.js
To use it…
mrt add router
In client/router.js:
Meteor.Router.add({
'/news': 'news', // renders template 'news'
'/about': function() {
if (Session.get('aboutUs')) {
return 'aboutUs'; //renders template 'aboutUs'
} else {
return 'aboutThem'; //renders template 'aboutThem'
}
},
'*': 'not_found'
});
In your template…
<body>{{renderPage}}</body>
I found the same problem. When the code gets bigger it is difficult to keep the code clean.
Here goes my approach to this problem:
I separate the different html pages as I would do with another web framework. There is an index.html where I store the root html page. And then for each big functional part I create a different template and place it in one different html. Meteor then merges them all. Finally I create a session variable called operation where I define what to show at each time.
Here goes a simple example
index.html
<head>
<title>My app name</title>
</head>
<body>
{{> splash}}
{{> user}}
{{> debates}}
</body>
then in splash.html
<template name="splash">
{{#if showSplash}}
... your splash html code goes here...
{{/if}}
</template>
then in user.html
<template name="user">
{{#if showUser}}
... your user html code goes here...
{{/if}}
</template>
and so on ...
In the javascript code then I check when to print each template using the Session variable, like this:
Template.splash.showSplash = function(){
return Session.get("operation") == 'showSplash';
}
Finally the Backbone Router manages this Session variable
var DebateRouter = Backbone.Router.extend({
routes: {
"": "showSplash",
"user/:userId": "showUser",
"showDebates": "showDebates",
// ...
},
splash: function () {
Session.set('operation', 'showSplash');
this.navigate('/');
},
user: function (userId) {
Session.set('operation', 'showUser');
this.navigate('user/'+userId);
},
// etc...
});
I hope this pattern is helpful for other Meteor developers.
This is my hacky solution to routing :
https://gist.github.com/3221138
Just put the page name as the template name en navigate to /{name}

Backbone.js how do i define a custom set of routes depending on a certain application state

Currently building an app that runs on mobile phones
not related to the issue at hand, but through a certain event
the app lands in a state, either online or offline (internet available on the phone or not)
the offline app is very limited, only a few screens available)
now stop me if you catch me doing something stupid or something that i could to a lot better,
but my first thought was to have the Router have a dynamic set of routes,
much like you can define a dynamic url property on a collection.
so instead of this:
var router = Backbone.Router.extend({
routes: {
'' : 'homeAction',
'categories' : 'categoriesAction',
...
},
homeAction: function(){ },
categoriesAction: function(){ }
});
i was thinking of this:
var router = Backbone.Router.extend({
initialize: function(){
this.on('app:togglestate', this.toggleRoutes, this);
},
toggleRoutes: function () {
var router = this;
if(App.onlineModus)
router.routes = { /* hash with online routes here */ };
else
router.routes = { /* hash with offline routes here */ };
},
routes: {
'' : 'homeAction',
'categories' : 'categoriesAction',
...
},
homeAction: function(){ },
categoriesAction: function(){ }
});
though that aparently breaks the whole app,
as the Backbone.history.start(); throws an error, cannot call function start from undefined.
leading me to believe i the routes object is somehow used upon initialization and cannot be changed on the fly.
am i possibly thinking to far?
should i achieve this some other way?
other idea's i had were:
having the routes exactly like url, where the routes argument is a function returning a hash, that didn't work either
and now i'm thinking totally differnt, something along the lines of testing if the app is in online or offline modus in every route's Action. though that seems too mutch i'd probably have to relay them all through a single Action, which only passes to the actual action if the route is accessible in offline modus? but i would not really have a clear idea on how to start with such a relay action without writing too mutch boilerplate code...
In order to dynamically update the routes you will need to make a call to _bindRoutes() after updating the routes.
For example:
toggleRoutes: function () {
var router = this;
if(App.onlineModus)
router.routes = { /* hash with online routes here */ };
else
router.routes = { /* hash with offline routes here */ };
// Get rid of previous navigation history
if(Backbone.history){
Backbone.history == null;
}
// Bind the new routes
router._bindRoutes();
}
Note that when you dynamically change the routes the history is no longer valid so you need to delete the previous history. When _bindRoutes is called it automatically instantiates a new Backbone.history when is called this.route.
I had to do something very similar. I don't have the code in front of me, but this should be right around what I did: (Edit: fleshed it out a bit so you can actually run it now)
ApplicationRouter = Backbone.Router.extend({
//some stuff
constructor: function (routes) {
this.routes = routes;
Backbone.Router.prototype.constructor.call(this);
}
});
routeObject = {
"help": "help"
}
ApplicationRouter.instance = function(routes) {
if (!this._instance) {
this._instance = new ApplicationRouter(routes);
}
return this._instance;
}
ApplicationRouter.instance(routeObject);
ApplicationRouter.instance().on('route:help', function() {
console.log('helped');
});
Backbone.history.start();
//now go to page#help - the console will say "helped"
From then on out, I just referenced ApplicationRouter.instance() when I needed access to the application router.

Categories