What is the best way of toggling the presence of some element (represented by a template) that is a direct child of <body>?
I'm talking about a modal box, notification, light-box etc. that is triggered either by some user event or by a route.
Examples: Dialog for newsletter sign-up that is shown after a user clicks a button. Overlay for content editing that is triggered by appending /edit to the item's route
Edit:
The first solution I though of was using Session to control state and then lining up all the application's modals and messages inside #if statements at the end of my main layout template like this:
<template name="layout">
<!-- yields and stuff -->
{{#if isActiveModal 'editArticle'}}{{> editArticle}}{{/if}}
{{#if ...
</template>
The problem is modularity; if a teammate is working inside some page template and needs to display a specific message or dialog, he or she has to edit the main layout to add it. I would have liked some easy way of conditionally appending a template to <body>.
Should be possible with the tools at hand, shouldn't it?
I actually use bootbox throughout several of my Meteor apps and it works great and does not interfere with any of Meteor's rendering. You can use the normal alert, confirm, prompt, as well as a custom dialog function, in a non-blocking way. There is a smart package available:
https://github.com/TimHeckel/meteor-bootboxjs
See some of my apps and smart packages for examples (grep for bootbox.):
https://github.com/mizzao/CrowdMapper/
https://github.com/HarvardEconCS/turkserver-meteor/
If you use the latest version of the new template engine, you can work with the body tag as its own special template through UI.body:
meteor update --release template-engine-preview-10.1
HTML:
<body>
{{#with alert}}
{{> modal}}
{{/with}}
</body>
<template name="modal">
<div class="modal">
X
<p>{{message}}</p>
</div>
</template>
JS:
if (Meteor.isClient) {
UI.body.alert = function() {
return Session.get("modal-alert");
};
UI.body.events({
"click .close": function() {
Session.set("modal-alert", null);
}
});
}
Since I posted the question, Blaze has become more sophisticated. Elements can now be rendered anywhere in the DOM while keeping a logical view hierarchy. See this discussion:
https://forums.meteor.com/t/most-useful-meteor-apis/1560/8
I've inserted the main outline below – the full thread also has code examples.
...
[Use] Blaze.render to output a template anywhere in the DOM – such as in a dimmable wrapper directly inside <body> – while maintaining a parent-child relationship between the view of the opening template and the view of the modal. This relationship is required for them to communicate directly.
(Bear in mind that a Session variable, among other limitations, can only hold serializable data...)
With the hierarchy intact, it's possible to set up shared reactivity between the modal and the background and call methods on the other, too. If we volunteered to use Blaze.renderWithData, then the two templates can even share the same data context!
Manually traversing the view hierarchy is pretty tedious, but with the parent() method from aldeed:template-extension, it isn't an issue.
Depending on whether one likes to use template instances as a 'view-model' or prefers to pass stuff around, reciprocal reactivity between the two templates can either be mediated by a new ReactiveVar assigned to a property on the parent template instance or view, or by a more retrieve-on-demand TemplateVar.
The entire exercise with Blaze.render can be duly incapsulated by using tricks like block helper templates and keyword arguments, so that everything is kept declarative.
Related
The Emblem syntax guide shows you can attach actions to an element like this
a click="toggleHeader" my plain text
which compiles to this in handlebars:
<a {{action toggleHeader on="click"}}>my plain text</a>
For multiple event listeners on the same element, can you do something like this
a click="toggleHeader" touchEnd="toggleHeader"
or
<a {{action toggleHeader on="click touchEnd"}}>x</a>
I've tried both, but neither work.
Is it even necessary to attach multiple event listeners when trying to make clicking in the browser behave the same way as touching in mobile or touch screens?
If Ember already has convenience methods that account for web and mobile events, can you point me to the reference. This link does not qualify because it just lists names, and doesn't tell you if any are composite event listeners.
Hopefully, as the line between laptops and touch screens become blurred, we'll eventually have vanilla jQuery that accounts for both types with one method (unless the combined packages exists already).
As this JSBin will show you, you can't have multiple events trigger the same action using the inline action helper. There's been discussions about adding this type of functionality, but I believe they've all ended with the Ember team suggesting you use a view instead of the inline action helper.
So I'm guessing the reason that Emblem won't output what you want is because it's not valid Ember code. I suggest using a view or a component to achieve what you want.
Quick note:
I don't believe this is a duplicate of Ember.js: Prevent destroying of views. Other related questions that I've found are out-of-date.
In case this becomes out-of-date later, I am using Ember 1.7.0 with Handlebars 1.3.0.
Context for the question:
As the title states, I am wondering how to transition between views without destroying them. Using queryParams does not solve my issue.
I am creating a calculator with the following nested views:
>>Calculator View
>>Report View (hasMany relationship to Calculator)
--School Partial (I am using queryParams here)
I am able to navigate between the Report views just fine without destroying my School partial, since I am using queryParams and using a displaySchoolPartial boolean to show/hide the partial. Example below:
Report template (stripped to only show the essential part):
<script type="text/x-handlebars" data-template-name="calculator/report">
...
{{#link-to "calculator.report" (query-parameters displaySchoolPartial="true")}}
{{render "_school"}}
</script>
School template (also stripped down):
<script type="text/x-handlebars" data-template-name="_school">
{{#with controllers.calculatorReport}}
<div {{bind-attr class=":schoolPartialWrapper displaySchoolPartial::hide-element"}}>
...
</div>
{{/with}}
</script>
This works as expected. Navigating between different Report views and School partials, as stated before, does not destroy the view.
The problem:
My problem comes when navigating to the Calculator view, the Report view is destroyed, which then destroys my School view. I do not want to also use queryParams to replace my Report views.
The reason I need to make sure the views aren't destroyed is because I have a select box with 3,000 schools in my School partial. It takes too long to re-render this. It would be a much better UX to simply show/hide the Report views.
Don't fight with Ember. You will lose.
Views are instantiated and rendered when needed and torn down when done.
Why do you have a 3000-element dropdown, anyway?
If you really, really want to do this, what I would suggest is putting a {{render}} on your application page, and hide it. The view will be created and rendered when the app comes up and persist as long as the app is alive. Then, in the didInsertElement of your view, do a cloneNode of that hidden element and insert it into the view's DOM somewhere. You may have to muck around getting event handlers wired up correctly.
My suggestion is not using "render" but using "partial", so you only need to drop in the template that you want. Have a control variable that set show/hide via css class. And control that variable using you controllers.
Using "partial" will allow you to have school template independent from report, thereby removing report will not affect school.
Just make sure you define the outlet and partial correctly.
Hope it helps!
I'm having a hard time coming up with a solution for this problem.
I'm building a WYSIWYG designer, for micro sites. The templates for these microsites will be supplied by an intermediate user, and the end-user will manipulate these templates. So, there are really two groups of users of the app: template-buiders, and end-users. Think MailChimp.
This means my Ember app will start off with a template from a template-builder, say
<h1>An awesome product</h1>
<h2 contenteditable="true">Subtitle away</h2>
<p>{{#if optionA}} One thing {{else}} Another thing {{/if}}<p>
and the end user, having chosen this template, will then be able to customize it. There are a few requirements:
There will be static, uneditable portions of the page (h1 above)
There will be static, editable portions of the page (h2 above)
There will be options that affect the layout, style, etc. (p above)
So far, my attempts have lead me to build a handlebars helper that takes a string and a context, and returns a rendered template. This is because the above template will actually be coming over from a database, as a string - remember, it's user-generated.
That means, in my application's template, it would look like
<div class="preview">
{{preview-userTemplate template context}}}
</div>
where
template: '<h1>An awesome product</h1><h2 contenteditable="true">Subtitle away</h2><p>{{#if optionA}} One thing {{else}} Another thing {{/if}}<p>',
context: {optionA: true}
Now, this actually works, in the sense that it will update if context is updated, so I can add controls along the sides for the end-user to manipulate those options. So I have those (more of less) under control.
It's the WYSIWYG content editing that is giving me trouble. In the above template (from my template-builder users), I want them to be able to add contenteditable="true" to places in their templates where the end-users can change the content.
Now, these changes need to be saved. Ideally, I would be able to retrieve the instance view's template back from the DOM, after the inline edits have been made. Is this possible? The original {{data}} placeholder would need to be preserved.
Alternatively, I could make a view/component, so the template-builder would do something like this:
<h1>An awesome product</h1>
{{#editable}}
<h2>Subtitle away</h2>
{{/editable}}
<p>{{#if optionA}} One thing {{else}} Another thing {{/if}}<p>
but this seems like it would require a good deal of finagling to get the edits to stick - I'm not even sure right now how I'd go about it.
What do you guys think? Is there an easier solution that I'm overlooking? Thanks for any pointers!
I'm working on an Ember.js app similar to this:
http://jsfiddle.net/rzb2m/
Now this does pretty much what I want except for one crucial problem. The setFavorite action at the bottom has a hard-coded 0. This means that if you change Carol's favorite activity, it actually changes Alice's favorite activity. I haven't been able to figure out how to structure the application so that it has the correct behavior. How do I pass the correct object to be changed (this.content.people[i] in this case) to the setFavorite function?
Note: My real application is actually a bit more complicated than this because I have two separate lists of people on the page. So the solution shouldn't just involve passing an index into setFavorite because that won't scale to the more complex situation with multiple lists. This is also why the person template looks unused. In the real app, there is complexity that justifies it.
You are relying on implicit binding resolution from controllers to views. But this breaks down because when you separate things out into partials you have no means to lookup the parent object in the #each iteration.
While #each controller works, you are better off with #each person in controller. Then you can access the properties from the view in the partial view, and pass it forward as needed.
Change the main #each in index template to,
{{#each person in people}}
<li>{{view App.PersonView contentBinding=person}}</li>
{{/each}}
Then in person template,
{{view App.PersonDetailsView contentBinding=view.content}}
And in personDetails template.
{{#each fave in view.content.getActivities}}
All this gives you the ability to pass the person to the setFavorite method,
<a {{action setFavorite view.content fave}} href="#">
So the setFavorite switches to a simple assignment on that person.
setFavorite: function(person, fave) {
person.set('favoriteActivity', fave.name);
},
Here's the updated jsfiddle.
I've been reading a lot on emberjs lately but something isn't really clear to me: I have a feeling that there are different ways of rendering a template. Can someone explain the differences between these:
{{render}}
{{partial}}
{{template}}
{{outlet}}
I'm using pre4, so if some of these keywords are obsolete, please notify.
You can search the Ember.JS source for all of these by searching for: Ember.Handlebars.registerHelper('?'. For example, to find the part where template is defined, search for: Ember.Handlebars.registerHelper('template'
{{template}}
Is similar to the {{partial}}, but looks for templates that you define in the Ember.TEMPLATES hash. From the source code we can see an example: Ember.TEMPLATES["my_cool_template"] = Ember.Handlebars.compile('<b>{{user}}</b>'); and then we can render it that way.
I heard a whisper that {{template}} is #deprecated, but I can't find where I found that information at the moment. However, it's worth mentioning that I've never found myself using this one. Instead I prefer {{partial}}.
Edit: It appears as though it isn't #deprecated as of
3df5ddfd4f. My mistake!
{{partial}}
This is different to the {{render}} approach in that the controller and view are inherited from the context that called it. For example, if you're in the UserRoute, and you load in a partial in your user template, then the UserView and UserController will both be passed to your partial, so they can access exactly the same information as its current parent.
Partial names, when defined, start with an underscore. For instance, a Profile partial will have the data-template-name of: data-template-name="_profile" but is inserted into your view as {{partial "profile"}}.
{{outlet}}
You'll probably find yourself using this one a lot. It's predominantly used in cases where the outlet changes frequently, based on user interactions. By transitioning to (this.transitionTo/{{#linkTo}}) another page, Ember inserts the view into the {{outlet}} and attaches the relevant controller and view.
As an example, if you're transitioning into /#/pets then, by default, Ember will load the PetsView into the {{outlet}}, and attach the PetsController, all of this after initialising the PetsRoute to take instructions before initialising the view and finding the controller.
{{render}}
This is a mixture of an {{outlet}} and a {{partial}}. It's used for static pages that don't switch out for other pages (as an outlet does), but it doesn't inherit the controller and view (as a partial does).
It's better with an example. Let's say you've got a navigation. Usually you'll only have one navigation, and it won't change for another one, but you want the navigation to have its own controller and view, and not to be inherited from the context (probably ApplicationRoute). Therefore when you insert the navigation ({{render "navigation"}}), Ember will attach App.NavigationController and App.NavigationView.
Summary
template: Consults a global hash and inserts the view when it finds it (possibly soon to be #deprecated);
partial: Used to split up complicated views, and inherits the controller/view from the parent (if you're in the UserController, then the partial will also have access to this, and its associated view).
outlet: Most widely used, and allows you to quickly switch out pages for other pages. Relevant controller/view attached.
render: Similar to an outlet, but is used for pages that are persistent across the entire application. Assumes the relevant controller/view, and doesn't inherit them.
Did I explain them well?
Just to clarify:
Partial: Inherited controller, inherited view, non-switchable;
Outlet: Relevant controller, relevant view, switchable;
Render: Relevant controller, relevant view, non-switchable;
The guide also provides some useful information here! Below is a quick summary:
I wanted to post another answer here that really helped me to clarify when to use the various template techniques -
Route
Using a route is when you need a full-blown route. A 'route' has a unique URL and consists of generated or user defined classes of the following type -
Route (Ember.Route)
Controller (Ember.Controller || Ember.ArrayController || Ember.ObjectController)
View (Ember.View)
Template (Handlebars template)
{{render}}
Use the {{render}} helper when you need a view but still need to provide some functionality with a controller. {{render}} does not have a unique URL and consists of the following -
Controller (Ember.Controller || Ember.ArrayController || Ember.ObjectController)
View (Ember.View)
Template (Handlebars template)
{{component}}
Use the {{component}} helper when you are building a commonly re-used template which exists independent of the context it is rendered within. An decent example may be if you were building a retail website and wanted to have a product view agnostic of where it is rendered. {{component}} does not have a unique URL nor a controller but instead has a component class and consists of the following -
Component (Ember.Component)
Template (Handlebars template)
{{partial}}
Use the {{partial}} helper when you are simply re-using some mark-up. {{partial}} does not have a unique URL nor any special backing like a component and consists of the following -
Template (Handlebars template)