Better errors with Ember - ember.js

Is there a way to have clearer error messages when something is wrong with ember?
For exemple, I have this error 05:10:32,332 Error: Assertion Failed: A helper named 'eq' could not be found1 vendor.self-4fd4ab06f1f66c1cec72e1ec3a2c99328df792e46fb1fdcd0258c341b30a7c3b.js:24472:0
. This error is not the subject of the question, this is just an example.
I have no idea where is eq. The console indicated this function :
function EmberError() {
var tmp = Error.apply(this, arguments);
// Adds a `stack` property to the given error object that will yield the
// stack trace at the time captureStackTrace was called.
// When collecting the stack trace all frames above the topmost call
// to this function, including that call, will be left out of the
// stack trace.
// This is useful because we can hide Ember implementation details
// that are not very helpful for the user.
if (Error.captureStackTrace) {
Error.captureStackTrace(this, _emberMetalCore.default.Error);
}
// Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
for (var idx = 0; idx < errorProps.length; idx++) {
this[errorProps[idx]] = tmp[errorProps[idx]];
}
}
This is not related to my problem.
Obviouly, I searched eq in my code and I have no results. I suppose this is in a module but using grep is very ineffective.
Sometimes there is a stacktrace but its not very efficient too. To find an addon or the source in my code in a big vendor.js or myapp.js is not ideal.
Is there a better solution?

I think something in one of your addons or other third party code is using the ember-truth-helpers addon.
vendor.js typically contains third party code you've imported, not code that you've wrote.
As to the basic issue, it is really up to the maker of the third party code you've imported to document its dependencies and to ensure they are installed when you install that dependency. This really is not a failing of Ember itself, it has told you that there is no helper named eq and has given you the line number in the precompiled template where the eq was used. You can use the sources tab in Chrome to scroll to line 24472 in vendor.self-4fd4ab06f1f66c1cec72e1ec3a2c99328df792e46fb1fdcd0258c341b30a7c3b.js

Related

In-repo addon writing public files on build causes endless build loop on serve

I'm having difficulty with my in-repo addon writing to appDir/public. What I'd like to do is write out a JSON file on each build to be included in the app /dist. The problem I'm running into is when running "ember serve", the file watcher detects the new file and rebuilds again, causing an endless loop.
I've tried writing the JSON file using preBuild() and postBuild() hooks, saving to /public, but after build, the watcher detects it and rebuild over and over, writing a new file again each time. I also tried using my-addon/public folder and writing to that, same thing.
The only thing that partially works is writing on init(), which is fine, except I don't see the changes using ember serve.
I did try using the treeForPublic() method, but did not get any further. I can write the file and use treeForPublic(). This only runs once though, on initial build. It partially solves my problem, because I get the files into app dist folder. But I don't think ember serve will re-run treeForPublic on subsequent file change in the app.
Is there a way to ignore specific files from file watch? Yet still allow files to include into the build? Maybe there's an exclude watch property in ember-cli-build?
Here's my treeForPublic() , but I'm guessing my problems aren't here:
treeForPublic: function() {
const publicTree = this._super.treeForPublic.apply(this, arguments);
const trees = [];
if (publicTree) {
trees.push(publicTree);
}
// this writes out the json
this.saveSettingsFile(this.pubSettingsFile, this.settings);
trees.push(new Funnel(this.addonPubDataPath, {
include: [this.pubSettingsFileName],
destDir: '/data'
}));
return mergeTrees(trees);
},
UPDATE 05/20/2019
I should probably make a new question at this point...
My goal here is to create an auto-increment build number that updates both on ember build and ember serve. My comments under #real_ates's answer below help explain why. In the end, if I can only use this on build, that's totally ok.
The answer from #real_ate was very helpful and solved the endless loop problem, but it doesn't run on ember serve. Maybe this just can't be done, but I'd really like to know either way. I'm currently trying to change environment variables instead of using treeforPublic(). I've asked that as a separate question about addon config() updates to Ember environment:
Updating Ember.js environment variables do not take effect using in-repo addon config() method on ember serve
I don't know if can mark #real_ate's answer as the accepted solution because it doesn't work on ember serve. It was extremely helpful and educational!
This is a great question, and it's often something that people can be a bit confused about when working with broccoli (I know for sure that I've been stung by this in the past)
The issue that you have is that your treeForPublic() is actually writing a file to the source directory and then you're using broccoli-funnel to select that new custom file and include it in the build. The correct method to do this is instead to use broccoli-file-creator to create an output tree that includes your new file. I'll go into more detail with an example below:
treeForPublic: function() {
const publicTree = this._super.treeForPublic.apply(this, arguments);
const trees = [];
if (publicTree) {
trees.push(publicTree);
}
let data = getSettingsData(this.settings);
trees.push(writeFile('/data/the-settings-file.json', JSON.stringify(data)));
return mergeTrees(trees);
}
As you will see the most of the code is exactly the same as your example. The two main differences are that instead of having a function this.saveSettingsFile() that writes out a settings file on disk we now have a function this.getSettingsData() that returns the content that we would like to see in the newly created file. Here is the simple example that we came up with when we were testing this out:
function getSettingsData() {
return {
setting1: 'face',
setting2: 'my',
}
}
you can edit this function to take whatever parameters you need it to and have whatever functionality you would like.
The next major difference is that we are using the writeFile() function which is actually just the broccoli-file-creator plugin. Here is the import that you would put at the top of the file:
let writeFile = require('broccoli-file-creator');
Now when you run your application it won't be writing to the source directory any more which means it will stop constantly reloading 🎉
This question was answered as part of "May I Ask a Question" Season 2 Episode 2. If you would like to see us discuss this answer in full you can check out the video here: https://youtu.be/9kMGMK9Ur4E

pnotify prompt dialog not showing in an ember project

I'm using the pnotify error and success dialogs without any trouble, but the prompt dialog doesn't want to show. I get a Uncaught TypeError: Cannot read property 'addClass' of undefined error.
Has anybody been successful in getting this to work?
jsbin: pnotify with ember
Here is a working example: http://jsbin.com/fuqoke/1/
So what is the problem. Well, Ember uses prototype extensions. This polyfills ECMAScript 5 array methods in browsers that do not implement them, adds convenience methods and properties to built-in arrays, and makes array mutations observable. The problem is that pnotify expects to work with normal oldschool javascript arrays. For example this code snippet where it wants to loop over an array:
...
for (var i in options.buttons) {
btn = options.buttons[i];
...
This loop will also iterate over the methods added to arrays by Ember (e.g. addObjects, firstObject,....)
So 2 ways to solve this:
1) You modify the pnotify code, and contribute it ;), with a more robust implementation to loop arrays
...
for (var i = 0; i < options.buttons.length; i++) {
btn = options.buttons[i];
...
or
...
for (var i in options.buttons) {
if(options.buttons.hasOwnProperty(i)){
btn = options.buttons[i];
...
2) You disable Ember's prototype extensions, which I don't recommend. Here you can find more info:http://emberjs.com/guides/configuring-ember/disabling-prototype-extensions/ but it will get you in a lot of trouble.
To be short it is definitely a bug in pnotify.

emberjs get('lastObject') bug?

I'm looping through an array using Ember.Array#lastObject property:
while (last = this.get('clickViews.lastObject')) {
// perform some actions to last
this.get('clickViews').removeObject(last);
}
When all objects from the clickViews array have been removed, this.get('clickViews.lastObject') still returns an object. Is there something wrong with how I'm getting 'lastObject' or removing objects from array?
Maybe you should do it this way. See here for a working jsbin.
while (this.get('clickViews.lastObject') !== undefined) {
// perform some actions to last
this.get('clickViews').removeObject(this.get('clickViews.lastObject'));
}
Hope it helps.
This seems to be a cacheing problem with multiple versions of emberjs loaded within the browser window context, as similar to the answer to this question: emberjs getEach method does not work as expected
I resolved problem by clearing browser history and rails precompiled tmp files.

"Cannot call method 'destroy' of undefined" in arrayWillChange

I have what I think is a pretty standard array/template relationship setup, but when I push a new item into the array I get the above mentioned Cannot call method 'destroy' of undefined error in the arrayWillChange method of the Ember source:
for (idx = start + removedCount - 1; idx >= start; idx--) {
childView = childViews[idx];
if (removingAll) { childView.removedFromDOM = true; }
childView.destroy(); <-- childView is undefined
}
I have never had this issue before. This doesn't happen when I remove an item from the array. Only on addition. Below is a link for a JSBin where I tried to duplicate the issue. The error doesn't get thrown but the template doesn't update either.
http://jsbin.com/asemul/2
EDIT:
You're calling array.push instead of array.pushObject -- the latter is an Ember.js method that is binding aware, which means it will automatically update bindings for you. The handlebars template helper {{#each filters}} is a binding to the filters array of the controller, and the template needs to know to update when the underlying array is updated. push doesn't tell the binding to update, but pushObject does.
Here's a working example (all I did was change push to pushObject): http://jsbin.com/asemul/6/
This is a pretty common mistake -- usually, I find that if my templates aren't synchronized with the underlying object, it's because something's wrong with the bindings, so that's where I start looking.
END EDIT
I don't think you should be setting removedFromDOM directly -- try using childView.remove() followed by destroy().
I'm not sure what the context is, but have you looked at ContainerView or CollectionView? Both of those views have support for arrays of child views and may accomplish what you're looking to do both more robustly and with less code.

Using hogan.js with express.js + vhosts

What is the correct way to use hogan.js with express.js?
I've tried the following:
var hogan = require('hogan.js')
...
app.set('view engine', 'hogan');
followed by
app.register('.hogan', hogan);
But I end up with the following error:
500 Error: Cannot find module 'hogan'
TJ put out a library called consolidate.js ( https://github.com/visionmedia/consolidate.js ) but I'm having trouble getting it to work with Express 2.5.8. After spending the day trying to figure this out I also came across a library called hulk-hogan.js ( https://github.com/quangv/hulk-hogan ) and another called hogan-express ( http://allampersandall.blogspot.com/2011/12/hoganjs-expressjs-nodejs.html ). But, do I really need all that?
If the solution can not be as simple as setting the templating engine with app.set() and app.register(), it would be great if someone could help me understand why. I'm using Hogan on the client and it's working great, it would just be so much better if I could also use it on the server.
UPDATE: Turns out there are two issues here.
While this is not causing the 500 error, Express does not work with Hogan out of the box (see: Linus G Thiel's answer below)
What seems to be causing the 500 error is that I'm using a virtual host and when I call res.render(), my res.render() call is actually calling the res.render() of a different virtual host on my same server.
Adding the full Express error dump. It looks like my app ('dataviz') is trying to use the render call from a different app ('datavizblocks')? Again, the two apps are virtual hosts on the same server.
dataviz 8000
Error: Cannot find module 'hogan.js'
at Function._resolveFilename (module.js:332:11)
at Function._load (module.js:279:25)
at Module.require (module.js:354:17)
at require (module.js:370:17)
at View.templateEngine (/localhost/datavizblocks/node_modules/express/lib/view/view.js:134:38)
at Function.compile (/localhost/datavizblocks/node_modules/express/lib/view.js:68:17)
at ServerResponse._render (/localhost/datavizblocks/node_modules/express/lib/view.js:417:18)
at ServerResponse.render (/localhost/datavizblocks/node_modules/express/lib/view.js:318:17)
at /localhost/dataviz/routes/section.js:325:7
at callbacks (/localhost/dataviz/node_modules/express/lib/router/index.js:272:11)
dataviz 8000
Error: Cannot find module 'hogan.js'
at Function._resolveFilename (module.js:332:11)
at Function._load (module.js:279:25)
at Module.require (module.js:354:17)
at require (module.js:370:17)
at View.templateEngine (/localhost/datavizblocks/node_modules/express/lib/view/view.js:134:38)
at Function.compile (/localhost/datavizblocks/node_modules/express/lib/view.js:68:17)
at ServerResponse._render (/localhost/datavizblocks/node_modules/express/lib/view.js:417:18)
at ServerResponse.render (/localhost/datavizblocks/node_modules/express/lib/view.js:318:17)
at /localhost/dataviz/routes/section.js:325:7
at callbacks (/localhost/dataviz/node_modules/express/lib/router/index.js:272:11)
The 500 error goes away when I comment out the datavizblock vhost, or when I switch the order of the vhost declarations around to have the dataviz vhost declared after datavizblocks vhost (of course, this then causes problems for the datavizblocks vhost)
Apologies ahead of time for the confusing question, but I was really confused when I came across this issue and never expected that switching to Hogan would have conflicts with virtual hosting.
The issue is that Express requires an interface from template engines, where the template engine is expected to have a compile method, and that compile method is expected to return a function which can be called with the template data.
Hogan has a compile method, but it returns a template object which has a render method. You need to expose that render method to Express, and this seems to be what the hogan-express module does. It shouldn't have to be that involved though, I think this will work (I have only tested it slightly, might be some gotchas?):
var express = require('express'),
hogan = require('hogan.js'),
app = express.createServer();
app.set('view engine', 'hogan');
app.register('hogan', {
compile: function() {
var t = hogan.compile.apply(hogan, arguments);
return function() {
return t.render.apply(t, arguments);
}
}
});
Basically, we are just creating our own object that has a compile method that maps to Hogan's render method.
This expects your templates to be named e.g. index.hogan.
As Linus said, you need an adapter to use Hogan with Express. consolidate works fine as long as you don't need support for partials or layouts (they are working on it but I don't know when it will be ready).
I was in the same spot you're in a few months ago and found hulk-hogan's and express-hogan's documentations to be quite confusing so I coded my own wrapper that has support for partials, layouts, template caching and can be plugged in Express in one line of code. You can check it out here: h4e - templating with hogan for express