I would like to see in the console some kind of output if a template doesn't get all {{properties}} (for debugging)
e.g. If a template
<script type="text/x-handlebars" id="demo">
<h1>{{name}}</h1>
<p>{{desc}}</p>
</script>
is called with a model
{
name:"Max",
}
there should be an output like console.warn
Template "demo" could not find property "desc"
Is there any helper, callback, ... that is called every time a Handlebars expression like {{property}} is called?
You can do whatever you like with javascript. But do it carefully. :)
There are many way to achieve your goal depending on what you really need and how many time you can spend on hacking ember/handlebars/ember-data packages.
First and easiest variant is to override render method of SimpleHandlebarsView/_HandlebarsBoundView, which displays simple binded values (there are few other views: for #each, with, etc). This is simple sequence: override method, call an old method, check the result of getting value for rendering, console.warn if result === undefined. You can't get template name inside that method, but you can get parent view for rendered value. So, I suggest to turn on LOG_VIEW_LOOKUPS to easily match views with templates.
Example of console logs:
Rendering application with default view <(subclass of Ember.View):ember226> Object {fullName: "view:application"}
Rendering index with default view <(subclass of Ember.View):ember251> Object {fullName: "view:index"}
Rendering <(subclass of Ember.View):ember251> , property is undefined: model.undefined
JSBin with example.
Another way is to override handlebarsGet method from ember-handlebars-ext package: https://github.com/emberjs/ember.js/blob/v1.7.0/packages/ember-handlebars/lib/ext.js#L75.
Third, if you need to catch accessing to undefined properties of Ember.Objects, you can override unknownProperty method for those objects.
If you need to check a rendered data quickly, you can use {{log model}} method in Handlebar's template.
You can get deeper into patching and rewriting core of Ember, of course. But I advise not to rely much on inner structure of libraries. Good luck!
No, they use Ember.get(context, propertyName) There is no metadata returned saying the property is defined vs undefined.
Related
What I want to do is to pass a string to {{component}} helper, but I want more control over what string it gets passed in at runtime.
e.g.
// getComponentToRender will get the current UI theme in the app,
// determine whether it's running on the mobile mode, then fetches the
// appropriate name of a component to render based on the type of the
// component. Just an example.
{{component getComponentToRender(ComponentType.Button)}}
I investigated this, it turns out this isn't possible (Otherwise, please correct me). Of course, there's computed property, but it doesn't take an argument.
Next, I looked into extending the helper. This stackoverflow question shows you can extend one, but I couldn't locate where the component helper is located in Ember. There doesn't seem to be any questions/documentation regarding extending existing helpers, either. This is what I would do with this approach.
// I can't find where the {{component}} helper is located.
import Component from './???'
export default Component.extend({
compute(componentType, hash) {
let componentName = getComponentToRender(componentType);
this._super(componentName, hash)
}
})
What did I miss? It'd be great if someone can put me into the right direction.
The only way of calling a function from a template is writing an helper. Actually your reference StackOverflow question is also mentioning about helpers. Writing an helper will solve your problem.
You don't need to extend the component helper. You need to compose it. As following:
{{component (getComponentToRender ComponentType.Button)}}
Here is the working twiddle.
I have a mixin for Ember components, named in-view, the job of which is to request that that the element be brought in view. It is provided an attribute whose value is an piece of content to be brought into view, and if that attribute matches the component's content then I call scrollIntoView or the equivalent. The code looks something like this:
// calling template
{{#each items as |item|}}
{{my-item content=item inViewItem=inViewItem}}
}}
// mixins/in-view.js
scrollIntoView() {
if (this.get('content') === this.get('inViewItem'))
this.get('element').scrollIntoView();
}.on('didInsertElement')
// components/my-item/component.js
import InView from 'mixins/in-view';
export default Ember.Component.extend(InView,
This works fine. The question I have arises when I want to change the item in view. I can have the in-view mixin observe the inviewItem attribute:
}.on('didInsertElement').observes('inViewItem')
and this also works, but seems like a bit of a code smell.
In addition, my actual code structure is that there is a controller which knows which item is supposed to be in view, and then its template calls a my-item-list component which displays the scrollable div containing the item list, and that in turn calls the my-item component. This means I have to pass the inViewItem attribute from the controller down through two levels, as in
// resource/controller.js
inViewItem: something
// resource/template.js
{{my-item-list items=item inViewItem=inViewItem}}
// components/my-item-list/template.js
{{#each items as |item|}}
{{my-item content=item inViewItem=inViewItem}}
}}
I could avoid this by having the my-item template hard-wired to access the inViewItem attribute on the controller:
scrollIntoView() {
if (this.get('content') === this.get('controller.inViewItem'))
this.get('element').scrollIntoView();
}.on('didInsertElement')
but that's another code smell; I don't want to build this kind of dependency on a specific controller field into the mixin. Instead I could possibly pass the component the name of the controller attribute to watch, but this seems unduly clumsy, and it's tricky to observe an attribute whose name is variable. More importantly, I don't think this will work when controllers go away in 2.0.
What I want essentially is a way to "ping" or somehow send a message to a template. I know that in principle this violates the DDAU principle, but in this particular case what I need is exactly to somehow send an "action down"--an action telling the component to adjust itself to bring itself into view.
Of course, I could give up on the entire idea of the in-view mixin and simply have the controller dig down into the generated HTML to find the item to bring into view and issue the scrollIntoView on it directly. However, this seems to violate some principle of separation of concerns; the my-item template would no longer be in complete control of itself.
What is the recommended design pattern for this kind of case?
The solution here is to go the opposite direction that you have. Your component here is a localized scope, and the pain you are feeling is that your localized scope needs to access and mutate global state (the app's scroll position).
Some people use a scroll-service for keeping track of and mutating state, I've used several variations on that myself.
It sounds though like you're dealing with a scrollable list, perhaps a div, and that what item is in view isn't merely a function of page state, but programmatically may change. For instance, a new item has been inserted and you want to scroll the new item into view.
A plugin like jquery.scrollTo or similar (collectively "scroller") would be better for that than simply jumping to the new position as it preserves the user's contextual awareness to where they are on page.
With a scrollable div or list or similar, you might choose to have your top level component control scroll state. The scroll state is still localized in this case, but instead of being localized to each item it's been localized to the scrollable region as a whole, which is where it better belongs.
There are a number of patterns for list items to register themselves with a parent list-component. In a robust scenario, I might do so, but a quick and not very dirty approach is to do something wherein on didInsertElement the new child emits an action to the parent containing it's context, which the parent then uses to check if it's the active item and if so triggers the scrollTo.
I am working on my first significant Ember.js app, and have been running into some roadblocks. After hitting one-too-many show-stopping bugs with ember-data, I have decided to roll my own models using Ember.Object (for now at least - ember-data looks like it will be real awesome, real soon).
My basic model structure is:
Album (has photo album-specific attrs, and references a collection of images)
Images (an ArrayProxy collection of Image models, which tracks collection-level attrs like 'currentImage', and 'nextImage')
Image
I have a {{#linkTo}} helper in my template that is supposed to allow the user to click to the next image in the set, like so:
<button>
{{#linkTo image controllers.images.nextImage}}
Next Image
{{/linkTo}}
</button>
This template has the context of the AlbumController, which needs: ["images"]. controllers.images.nextImage is a computed property, that figures out ID of the currently-displayed image, then uses it to find the Image model for the next model in the Images ArrayProxy collection.
My problem is this:
Upon page load, I receive an error message Uncaught Error: assertion failed: Cannot call get with 'id' on an undefined object.
I'm assuming this is because the {{#linkTo}} helper is trying to get the id property from the return of controllers.image.nextImage, which is a computed property that relies on the Images collection being loaded from the server. This async behaviour is being handled with promises behind-the-scenes, but the {{#linkTo}} helper seems to required a valid context to be returned immediately upon page load.
My questions are these:
Has anybody else had to handle this kind of situation, where they're not using ember-data and had to use {{#linkTo}} helpers with computed properties that weren't immediately available?
Can anyone suggest workarounds that don't fight Ember's way of doing things?
Some of my thoughts are:
make the computed property return a dummy context, that somehow gets replaced with a valid model after load
use an {{#action}} helper instead of {{#linkTo}}
I have written up a JSBin example which is mostly code-complete, except I couldn't manipulate the hash URL to trigger the nested routes, so I had to do everything in the application template.
I've got the need for a recursive nested set of views in Ember. Basically think of it as a file structure with folders.
I thought I could do it with render, but that didn't work. Inside structures/show view:
{{#each child in structures}}
{{ render 'structures/show' child }}
{{/each}}
I get the error:
Uncaught Error: assertion failed: This view is already rendered
I read in another SO Question about using the {{view}} helper:
{{#each child in structure.structures}}
{{ view App.StructuresShowView structureBinding='child'}}<br>
{{/each}}
But this doesn't render properly. Possibly because the example is too old?
How do I properly render the same view (and controller?) with a different object in order to build a nested/recursive view? Is there a better way than the object directions?
Thanks.
Update. Looks like itemControllerClass might be what I'm looking for? Still trying to figure it out.
I think you cannot call {{render}} multiple times on a single route. If you need to create something many times, you probably need to use {{control}}, which is the same as {{render}} with a difference that {{render}} has one controller (singelton) and {{control}} has a new controller every time it's called.
Here's an open issue related to your question.
Is there any way in Ember to update the Handlebars template content and re-render the dependent views dynamically?
I tried by using Ember.TEMPLATES and Ember.Handlebars.compile method, but it didn't worked and the JSFiddle is available here, so any suggestions?
I don't know why you're attempting to do this, but if it's just for testing sake, here is a working fiddle http://jsfiddle.net/VTP4n/2/.
Ember caches the template inside the view as a computed property, so I'm overriding it and calling rerender on the view. I wouldn't even consider using this in production though.
Up until recently, it was as easy as overriding the template and then calling view.notifyPropertyChange('template'), but with the new container stuff, it's a lot more complex to do it cleanly.
Capture anything you want the user to manipulate in the template as a property of the view/controller and create a binding for it either as computed property or attach an observer to it. This way you can create a view dynamically and append it anywhere you want in your document.