Verbose Logging in Ember - javascript

I'm trying to wrap my head around Ember at the moment, but all the magic is making this difficult.
I've set LOG_TRANSITIONS: true and Ember.LOG_BINDINGS = true; which gives me some minimal logging to the console, but I really need more than that.
I'm particularly struggling with seeing what's going on when Ember is automagically creating Controllers, Views and Templates.
Is there a way to log this aspect of the framework - to see where Ember is looking for Templates/Views/Controllers and when it is creating one on its own volition.
For example, I have the following routes set up:
App.Router.map(function() {
this.route("example_items", {path: "/"});
});
with:
App.ExampleItemsRoute = Ember.Route.extend({
model: function() {
return App.ExampleItem.find();
}
});
Ember renders my ApplicationController and its application.handlebars template:
<header class="page-header">
<h1>Application Template</h1>
</header>
{{outlet}}
But fails to render my example_items.handlebars template. I get no exception or warning, and if I check the DOM, I can see ember has created a generic view in its place.
The bindings logging shows me that Ember has transitioned to example_items, but it seems it hasn't used either my ExampleItemsController, ExampleItemsView or template.
How can I debug a situation like this if I receive no errors or messages?
Edit:
App.ExampleItems View:
App.ExampleItemsView = Ember.CollectionView.extend({
templateName: 'example_items'
});
And App.ExampleItemsController:
App.ExampleItemsController = Ember.ArrayController.extend({
});

I'm particularly struggling with seeing what's going on when Ember is automagically creating Controllers, Views and Templates.
Is there a way to log this aspect of the framework - to see where Ember is looking for Templates/Views/Controllers and when it is creating one on its own volition.
Yes. With the latest ember you can now LOG_ACTIVE_GENERATION to see console.log output whenever ember generates something for you.
Another new setting that might be helpful is LOG_VIEW_LOOKUPS

Here's your problem: CollectionView won't use your template. It takes an array as its content property (usually set up as a binding to the controller) and creates childViews manually. Without a content set it'll appear as a blank view.
If you add classNames: ['my-view'] to your view definition, you should see that the view it's instantiating and inserting is actually your view class, just empty. Add contentBinding: 'controller' and it should render itemViews for each item in the array, as well.

Related

Knockout binding custom components to not conflict with central view model

I am trying to create an application with custom components that interact with central service providers, which works fine.
You can see an example at https://github.com/brianlmerritt/knockout-babel-browserify-gulp
It's written in ES2015, and compiles fine.
The problem is that I want to be able to load a central view model that applies to each page except of course to the component which has it's own view models.
I thought I understood http://www.knockmeout.net/2012/05/quick-tip-skip-binding.html but when I surround the component with <!-- ko stopBinding : true --> the component fails to bind. If I don't surround the component then I get a binding conflict.
If someone can point me to how to register a component in a way that the central view model doesn't conflict I would be very grateful!
Each custom component is registered with:
ko.components.register(
'component-one',
require('./components/component-one/component.js'));
To keep the central view model simple, I just went with:
var centralViewModel = function centralViewModel()
{ var bindingWorked = ko.observable(
'The binding worked. KO view model centralViewModel has a this context and was bound to the html correctly');
};
Sadly when I do the bindings they conflict:
ko.applyBindings(centralViewModel(),document.body);
ko.applyBindings(); // Pull in all of the components
I am sure there must be a way to pull in the components in a way that does not conflict with the central view model.
It seems the conflict was actually averted correctly by applying centralViewModel to the DOM. There was no conflict with the components. The problem was in the centralViewModel.
Corrected code:
ko.components.register(
'component-one',
require('./components/component-one/component.js')
);
ko.components.register(
'component-two',
require('./components/component-two/component.js')
);
ko.components.register(
'component-three',
require('./components/component-three/component.js')
);
var centralViewModel = {
bindingWorked : ko.observable('The binding worked. KO view model centralViewModel has a this context and was bound to the html correctly')
};
ko.applyBindings(centralViewModel,document.body);

Nothing handled the event in Ember

I am getting a basic feel for Ember, and running into some weird issues at the moment. The app is pretty basic (and ugly). On a specific route, (/stack) , I list out a bunch of cards that belong to this stack. StackController is an array controller since it retrieves a stack of cards that belong to it, essentially
StackController = Ember.ArrayController.extend();
and
StackRoute = Ember.Route.extend({
model: function(){
return App.Card.find();
}
});
Then in my templates, I simply loop through it using the #each helper to display a property of the card ('front'), like this:
<ul>
{{#each}}
<li>{{front}}</li> <button {{action 'backside' this}}>View</button>
{{/each}}
</ul>
Up until here, everything is working the way it should. However, when I click on the 'View' button to trigger the 'backside' event, it yields a :
"Nothing handled the event 'backside' " error. Kind of bummed because it seems pretty basic that it should do so, oh yes, I do explicitly specify the 'backside' event in my Controller, like this:
StackController = Ember.ArrayController.extend({
actions:{
backside:function(){
alert("backside event handled");
}
}
});
For a more detailed look, here is the jsBin:
http://jsbin.com/AHiTicU/5/edit
What am I doing wrong?
The code in the JSBIN is accurate, there maybe some typos here however (unlikely, but a heads up).
You are using the ember-1.0.0-rc.6, in that version to use the action you need to put you action handler inside of the route, in a object with a key called events, like the following:
App.StackRoute = Ember.Route.extend({
model: function(){
return App.Card.find(); // must substitute this with this.store.find('card');
},
events:{
backside:function(){
alert('working');
}
}
});
Updated jsbin http://jsbin.com/AHiTicU/6/edit
I recommend you to use the lastest version, at the moment 1.2.0, you can get it in the home page of emberjs website http://emberjs.com/.
So your current code will work http://jsbin.com/AHiTicU/9/edit

Multiple Instances of the Same Controller Simultaneously in Ember

Just been watching the Ember Peepcode video. One thing it has hammered home to me is that Controllers are singletons, so a single instance of each Controller is created at runtime and the controller's data property is swapped in/out as needed.
But what happens when you need multiple versions of the same controller on screen and active at the same time. What happens if I have multiple example.handlebars templates, each of which needs to be backed by its own version of ExampleController on screen simultaneously?
How does Ember handle this situation?
There are several ways to handle that (mentioned in my previous answer).
Method 1:
{{render}} with a model (needs latest Ember.js build):
{{render "example" example1}}
{{render "example" example2}}
Method 2:
Update July 7 2014: {{control}} has been removed from Ember >= 1.0.
{{control}} (it is still buggy so avoid if you can)
{{control "example"}}
But first you need to enable the flag: ENV.EXPERIMENTAL_CONTROL_HELPER = true before loading ember.js file.
There's also a bug which you'll need to fix by doing:
App.register('controller:example', App.ExampleController, {singleton: false }
Method 3:
Using {{each}} with itemController.
{{#each controller itemController="example"}}
{{view "example"}}
{{/each}}
Each of these will create a new separate instance every time.
What I've found is that if you override the init method of the controller and set content to a fresh object (or whatever you're using), you can have multiple independent instances.
App.MyMultipleController = Ember.ObjectController.extend({
content: {},
init: function(){
this.set('content', {});
return this._super.call(this, arguments);
}
});
Here's a JSFiddle. It's using ObjectProxy, for my own purposes, but you could use ObjectController if you wanted: http://jsfiddle.net/jgillick/D83d8/

Backbone.js: Routing for nested views

I'm trying to figure out following scenario:
Lets say that I have two views: one for viewing items and one for buying them. The catch is that buying view is a sub view for viewing.
For routing I have:
var MyRouter = Backbone.Router.extend({
routes: {
'item/:id': 'viewRoute',
'item/:id/buy': 'buyRoute'
}
});
var router = new MyRouter;
router.on("route:viewRoute", function() {
// initialize main view
App.mainview = new ViewItemView();
});
router.on("route:buyRoute", function() {
// initialize sub view
App.subview = new BuyItemView();
});
Now if user refreshes the page and buyRoute gets triggered but now there is no main view. What would be best solution to handle this?
I am supposed that the problem you are having right now is that you don't want to show some of the stuff inside ViewItem inside BuyView? If so then you should modularized what BuyView and ViewItem have in common into another View then initialize it on both of those routes.
Here is a code example from one of my apps
https://github.com/QuynhNguyen/Team-Collaboration/blob/master/app/scripts/routes/app-router.coffee
As you can see, I modularized out the sidebar since it can be shared among many views. I did that so that it can be reused and won't cause any conflicts.
You could just check for the existence of the main view and create/open it if it doesn't already exist.
I usually create (but don't open) the major views of my app on booting up the app, and then some kind of view manager for opening/closing. For small projects, I just attach my views to a views property of my app object, so that they are all in one place, accessible as views.mainView, views.anotherView, etc.
I also extend Backbone.View with two methods: open and close that not only appends/removes a view to/from the DOM but also sets an isOpen flag on the view.
With this, you can check to see if a needed view is already open, then open it if not, like so:
if (!app.views.mainView.isOpen) {
//
}
An optional addition would be to create a method on your app called clearViews that clears any open views, perhaps with the exception of names of views passed in as a parameter to clearViews. So if you have a navbar view that you don't want to clear out on some routes, you can just call app.clearViews('topNav') and all views except views.topNav will get closed.
check out this gist for the code for all of this: https://gist.github.com/4597606

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}

Categories