Rendering to dynamic outlets - ember.js

I am trying to make a list of items that when clicked, shows an edit form inline with the list item.
So far this is what I've tried:
router.js.coffee
App.Router.map ()->
#resource 'friend_management', ->
#resource 'groups', ->
#resource 'group', path: '/:group_id', ->
#route 'edit'
templates/groups.hbs
<div id="group-list">
{{#each}}
{{#view Dfw.GroupView}}
{{link-to name 'group.edit' this class=cssClass tagName='div'}}
{{outlet groupEditOutletName}}
{{/view}}
{{/each}}
</div>
<!-- it works fine if there is one outlet rendered here,
but I would prefer the outlet for the edit form rendered inline -->
templates/group/edit.hbs
{{input type='text' value=name placeholder="Group name"}}
<button {{action update}}>Update Group</button>
routes/group/edit.js.coffee
App.GroupEditRoute = Ember.Route.extend
renderTemplate: (controller, model)->
#render('group/edit', outlet: "group#{model.id}", into: 'groups')
I get the impression that Ember.js doesn't allow for dynamic named outlets, but does anyone know a work around?

One option you have would be to place the editing UI that you want to be placed inline as part of the actual view. This would remove the ability to have the action still be router based, so you wouldn't be able to have group/edit/ID, but you would be able to edit inline.
Dfw.GroupView = Ember.View.extend({
templateName: 'groupView', // or whatever you call it
isEditing: false,
click: function(evt) {
if (!this.get('isEditing')) {
this.set('isEditing', true);
}
},
actions: {
update: function (e) {
// Update code here
this.set('isEditing', false);
}
});
Then your template for the group view could be something like this:
<div {{bind-attr class=cssClass}}>
<!-- HTML that will be shown even when not editing goes here -->
{{#if view.isEditing}}
{{input type='text' value=name placeholder="Group name"}}
<button {{action view.update}}>Update Group</button>
{{/if}}
</div>
I hope this helps. While it does not use the routing, it seems as though using routing in this case wouldnt make much sense, because you are viewing all of the objects together, instead of viewing one of them and then clicking edit.
Good luck!

Related

Emberjs: show outlet on certain conditional

I want to show some blocks of HTML based on the existence of an outlet. I tried this code but it doesn't work. Am I missing something?
{{#if outlet}}
<table class="col-sm-9">
some content
</table>
<table class="col-sm-3">
{{outlet}}
</table>
{{else}}
<table class="col-sm-12">
some content
</table>
{{/if}}
EDIT:
My scenario is if I'm on route customers then outlet is hidden, else if I'm on route customers.create then outlet is show. Is there any simple way to do it without touch router.js file?
Have you tried naming the outlet and then render the content of a particular outlet in the router.
Please see http://emberjs.com/guides/routing/rendering-a-template/
something like this , which is mentioned in the guide.
App.PostRoute = App.Route.extend({
renderTemplate: function() {
this.render('favoritePost', { // the template to render
into: 'posts', // the template to render into
outlet: 'posts', // the name of the outlet in that template
controller: 'blogPost' // the controller to use for the template
});
this.render('comments', {
into: 'favoritePost',
outlet: 'comment',
controller: 'blogPost'
});
}
});

Creating a Lightbox Route in Ember.js

My authentication system uses lightboxes, so that when a user clicks "Sign In" or "Sign Up", a lightbox pops up for them to input their credentials. The page they were on remains rendered behind the lightbox, and when they're done signing in, the lightbox disappears and the view returns to the way it was. I can get this to work when I deviate from the conventional Ember route flow by using a lot of Jquery, but I'd prefer to integrate this more tightly into the rest of my Ember app.
The problem is, the conventional Ember route flow expects views and templates to be handled in a particular way. Specifically, a route such as /sign-in will render the sign-in template within the application template, erasing whatever was there before. Since I want to preserve the view that was there before, this approach doesn't work.
Is there a way to tell an Ember view not to erase the current view, but instead to render an independent view such as a lightbox?
You can use named outlets and render a template into the outlet, in my aplication template I has an outlet called modal, and two actions in the ApplicationRoute, openModal and closeModal. The open one receives a template name and uses the route method render to set the outlet content, the close one renders an empty template.
App.ApplicationRoute = Ember.Route.extend({
actions: {
openModal: function(modal) {
this.render(modal, {into:'application', outlet: 'modal'});
},
closeModal: function() {
this.render('empty', {into: 'application', outlet: 'modal'});
},
}
});
Html handelbars
<script type="text/x-handlebars" data-template-name="application">
{{! Other application template code}}
<button {{action openModal 'hellow.modal'}}>Open it!</button>
{{outlet modal}}
</script>
<script type="text/x-handlebars" data-template-name="empty"></script>
<script type="text/x-handlebars" data-template-name="hellow/modal">
<div class="modal">
<div class="modal-header">
Hellow
</div>
<div class="modal-footer">
<button {{action closeModal}}>Close</button>
</div>
</div>
</script>
This is adapted from http://nerdyworm.com/blog/2013/04/20/ember-modal-example/

Can't swap currentView of Ember.ContainerView in a #each

I'm trying to use the the currentView feature of an Ember.ContainerView in the context of a #each helper but it fails when currentView property is changed to another view.
My aim here is to allow editing an item of a list, by changing the regular view to an edit view when the user click a link.
Main template:
<ul>
{{#each itemController="person"}}
<li>{{view Ember.ContainerView currentViewBinding="cv"}}</li>
{{/each}}
</ul>
Template 'name' used to display a person :
{{firstName}} {{lastName}} <a {{action edit}}>edit</a>
Controller for the currentViewBinding property ('cv') and handling for the edit action.
App.PersonController = Ember.ObjectController.extend({
cv: Ember.View.extend({
templateName: 'name'
}),
edit: function() {
this.set('cv', Ember.View.extend({
templateName: 'nameEdit'
}));
}
})
'nameEdit' template corresponding to the view that needs to be displayed to edit the person object.
{{input type='text' value=firstName}} {{input type='text' value=lastName}}
The api guide says that:
When the currentView property is set to a view instance, it will be added to the ContainerView. If the currentView property is later changed to a different view, the new view will replace the old view.
But it's worse if I replace the cv property with a view instance (by using create() instead of extend()) as a re-render error is yield. See this question of mine.
Here is the jsFiddle to play with http://jsfiddle.net/fblanvil/tD3Ph/3/
I ended up not using ContainerView at all and using a simple if. But it doesn't explain why it's not possible to use a ContainerView this way in an #each helper. If someone thinks it's worth a Jira, put a comment and I'll do it.
<ul>
{{#each itemController="person"}}
<li>
{{#if editing}}
{{view templateName='nameEdit'}}
{{else}}
{{view templateName='name'}}
{{/if}}
</li>
{{/each}}
</ul>
Simple and effective after all...
App.PersonController = Ember.ObjectController.extend({
editing: false,
edit: function() {
this.set('editing', true);
}
})

Adding a new object to an ArrayController

I am attempting to create a basic CRUD setup for managing 'User' objects in Ember. I think I have my models and routes in order. I'm struggling with managing:
A) The proper controller setup for the (all) users page. I think that I should be creating an ArrayController, but it seems to work fine automatically. Does my Ember App know to make an array of individual 'user' objects? if so, how?
B) Passing data from InputFields. If you click 'Add User' in my example, I have made a form to create a user. When you click 'create', I'm not sure how to get the textField values, nor do I understand what to do with those values once I have them. How would I update my controller?
Again, here a jsbin of my code. Any guidance would be greatly appreciated.
Regarding A):
I assume you refer to the following route of your App. This model function returns an Array. Therefore Ember knows that it should use an ArrayController to render your UsersRoute.
App.UsersIndexRoute = Ember.Route.extend({
model: function() {
return App.User.find();
}
});
Regarding B): I have updated your code -> http://jsbin.com/ozilam/15/edit
I needed to update some of your names (controller and view) to match the naming conventions of Ember.
With Ember you do not have to use forms and manually read those values. Instead you create a new records, when you enter your Route:
App.UsersNewRoute = Ember.Route.extend({
setupController : function(controller){
controller.set("content", App.User.createRecord({}));
}
});
Inside your View you are binding on the properties of your record. As you see i also updated your button with an action helper.
<script type="text/x-handlebars" data-template-name="users/new">
<div class="row">
<div class="six columns">
<h3>New User Information</h3>
<form>
<label>First Name</label>
{{view Ember.TextField valueBinding='name_first'}}<br />
<label>Last Name</label>
{{view Ember.TextField valueBinding='name_last'}}<br />
<label>Email Address</label>
{{view Ember.TextField valueBinding='email_address'}}<br />
<label>SSN</label>
{{view Ember.TextField valueBinding='ssn'}}<br />
<button {{action create target="view"}} class="button">Create</button>
{{#linkTo users}}Cancel{{/linkTo}}
</form>
</div>
</div>
</script>
As those changes in the form get automatically propagated to your controller, you can just access the object in the controller:
App.UsersNewView = Ember.View.extend({
didInsertElement: function() {
this.$('input:first').focus();
},
create: function(){
console.log('view submitted');
var newUser = this.get("controller.content");
console.log(newUser.get("name_first"));
console.log(newUser.get("name_last"));
// HOW DO I PROCESS THIS FORM
}
});
Note: As i am writing this i am realizing that it would be probably better, if you would handle this event in your Controller instead of the View, since its an data modification task.

Ember.js Binding without a view

Sorry If something like this has already been asked, but I can't find exactly what I am looking for. I'd like to preface the question by saying that I feel I have a decent understanding of content bindings. I plan on using a third party jquery carousel plugin that requires a list of img tags in a div in order to work properly. On to the actual question, let's say I have a collection of urls to images in an App controller. Assume that content will contain a list of valid urls to actual images.
App = Ember.Application.create({});
App.Controller = Em.Object.create({
content: [{url: 'http://www.google.com'}, {url: 'http://www.yahoo.com'}]
});
App.ImgView = Em.View.extend({
tagName: 'img'
});
How do I bind the src of each image to the current url without nesting another {{view}} in the #each? I've tried many combinations, but haven't been able to put my finger on the correct bindings.
<div id="foo">
{{#each App.Controller.content}}
{{view App.ImgView bindAttr src="this.url"}}
{{/each}}
</div>
The handlebar script above will error out, but I feel like it's the best why I can communicate what I am trying to do.
Thanks in advance for any help.
EDIT: After some more research I came across this issue here. Apparently srcBinding to a string was a bug in ember-0.9.4, and has been fixed in ember-0.9.5. I ended up going back to something like...
App.ImgView = Em.View.extend({
tagName: 'div'
});
<div id="foo">
{{#each App.Controller.content}}
{{#view App.ImgView contentBinding="this"}}
<img {{bindAttr src="content.url"}} />
{{/view}}
{{/each}}
</div>
so I could have a click handler on my view. I also modified the plugin to also target images inside of divs inside of #foo.
Thanks for all answers.
It seems like you want:
<div id="foo">
{{#each App.Controller.content}}
<img {{bindAttr src="url"}}>
{{/each}}
</div>
Does that work for you?
You should use srcBinding on your {{view ... }}. See example: http://jsfiddle.net/M2S3Z/
If you just want to render an image and don't need a specific Ember.View which renders the img tag, I would use ebryn's answer.
Template:
<script type="text/x-handlebars" >
<div id="foo">
{{#each App.Controller}}
{{view App.ImgView srcBinding="url"}}
{{/each}}
</div>
</script>​
JS:
App = Ember.Application.create({});
App.Controller = Em.ArrayProxy.create({
content: [{
url: 'http://www.google.com'},
{
url: 'http://www.yahoo.com'}]
});
App.ImgView = Em.View.extend({
tagName: 'img',
didInsertElement: function() {
console.log(this.get('src'));
}
});​
I would rather use something leveraging power of Ember views like this:
App.ImageView = Ember.View.extend({
tagName: 'img',
attributeBindings: ['src'],
src: url_attribute_or_variable
});