Ember.js View not properly passing context to router even - ember.js

I am having a bit of trouble with ember.js.
I have the following code which properly calls the event in the router to create the notebook. However, it will not pass the notebook context, it it undefined. I have been searching for hours to try and find a solution for this.
I found this and this, which are helpful but I'm not completely sure I'm on the right track. Am I missing something?
Route
App.NotebooksNewRoute = Ember.Route.extend
model: ->
App.Notebook.createRecord()
events:
create: (notebook) ->
#persist notebook
Form
{{#with content}}
<form {{action create content on="submit" }} >
<div>
{{view Ember.TextField placeholder="Enter a title" valueBinding="title" }}
</div>
<div>
{{view Ember.TextArea placeholder="Notes" valueBinding="description" }}
</div>
<button type="submit">Create</button>
</form>
{{/with}}

Am I missing something?
Change {{action create content on="submit" }} to {{action create this on="submit" }}
Why?
When you use the handlebars helper {{#with}}, the enclosed block will be rendered in the context of the specified variable. So after {{#with content}}, this is whatever content was and you can access properties like title and description directly instead of content.title and content.description

Related

Passing variables to handlebars partials in Ember.js

What I want to do should be fairly simple.
I want to pass variables to partials for reusability.
I want to do something like this :
<form {{action login content on="submit"}}>
<fieldset>
{{partial 'components/field-email' label="Email" fieldname="email" size="full"}}
[...]
</fieldset>
</form>
Instead of doing this :
<form {{action login content on="submit"}}>
<fieldset>
<div {{bind-attr class=":field :email size"}}>
<label {{bind-attr for=fieldname}}>{{label}}</label>
{{input type="email" id=fieldname name=fieldname valueBinding="email" placeholder=label}}
</div>
[...]
</fieldset>
</form>
coming from Rails, I expected this to just work, but it seems I can't (don't know how to) pass variables to a partial. I looked at all the ways to "include a template part":
partial
view
render
The thing that worked for me is using a View. But I thinks it's overkill. I just want separate sub-templates for reusability and readability, no context change or specifying a controller needed here.
Edit:
I also tried to use this partial as a component :
{{field-email type="email" id="email" name="email" valueBinding="email" placeholder=label size="full"}}
Which works for everything except the valueBinding.
I guess it's also worth mentioning that I have a route setup with an action that calls login on my AuthController :
App.LoginRoute = Ember.Route.extend
model: -> Ember.Object.create()
setupController: (controller, model) ->
controller.set 'content', model
controller.set "errorMsg", ""
actions:
login: ->
log.info "Logging in..."
#controllerFor("auth").login #
This whole thing works if all the markup is in the login template but fails if I try to break it up with partials, components and such.
There has to be something that I didn't see...
You should use a component in this case.
If you setup your template correctly (components/field-email), you can use on this way:
{{field-email label="Email" fieldname="email" size="full"}}
You could setup the html component properties, if you define the component. Based on your example, it could be:
App.FieldEmailComponent = Ember.Component.extend({
classNames: ['size'],
classNameBindings: ['email', 'field'],
field: null,
email: null
});
Example: http://emberjs.jsbin.com/hisug/1/edit
Got it working, I had to use a component. I had messed up the "value" part.
components/field-email.hbs :
<div {{bind-attr class=":field :email size"}}>
<label {{bind-attr for=fieldname}}>{{label}}</label>
{{input type="email" name=fieldname value=value placeholder=label}}
</div>
login.hbs :
<form {{action login content on="submit"}}>
<fieldset>
{{field-email label="Email" fieldname="email" value=email size="full"}}
[...]
</fieldset>
</form>
What I get from this is that in order for attributes to be used in a component they have to be explicitly set when using the component. Once they are set, they are bound.
In my case, when the input value changes, the associated route property is updated as well which is pretty cool.

How to set itemController for instance variable in ember.js indexController?

I have a Ember.js page that displays a table of items and shows details of one of the items when it is selected. The controller looks like this:
CV.IndexController = Ember.ArrayController.extend({
itemController: "CurrVitae",
selectedCurrVitae: false,
actions: {
selectCurrVitae: function(currVitae) {
this.set('selectedCurrVitae', currVitae)
}
}
});
And the index controller is used in a template like this:
<div class="container">
<div class="curr-vitae-list">
{{#each curr_vitae in controller}}
<div class="curr-vitae-item row">
<div class="col-sm-2" {{action selectCurrVitae curr_vitae}}>
{{curr_vitae.name}}
</div>
<div class="col-sm-2">
<!-- NOTE: This method is defined on the item
controller (not the model) so the itemController is
available at this point. -->
{{curr_vitae.createdAtDisplay}}
</div>
<div class="col-sm-7">
<div class="embedded-cell">
{{curr_vitae.summary}}
</div>
<div class="embedded-cell">
{{curr_vitae.objective}}
</div>
</div>
</div>
{{/each}}
</div>
<div class="curr-vitae-view">
<h2>Details</h2>
<!-- EDIT: I have tried setting this
as {{#if selectedCurrVitae itemController="currVitae" }}
to match the way the #each handles item controllers but that did
not seem to work -->
{{#if selectedCurrVitae }}
<!-- NOTE: Down here, however, the item controller is not available
so I can't use methods defined on the item controller for the
currently selected instance. -->
{{ partial "cv_index_details" }}
{{/if}}
</div>
</div>
Question: The problem I'm running in to is that the itemController I've set in the index controller is not available when rendering the selectedCurrVitae in the cv_index_details.
More details:
Specifically, in the partial I want to reuse a editor component (taken from Noel Rappin's Ember.js book). So if the cv_index_details partial looks like this:
<h3 class="selected_cv">{{selectedCurrVitae.name}}</h3>
<div class="row selected_cv_summary">
<h4>Summary</h4>
{{block-editor emberObject=selectedCurrVitae propName="summary" action="itemChanged"}}
</div>
<div class="row selected_cv_experiences">
<h4>Experiences</h4>
{{#each experience in selectedCurrVitae.experiences itemController="experience"}}
{{ partial "experience_detail" }}
{{/each}}
</div>
So in this template, the itemChanged action is not found for the selectedCurrVitae instance. However, I use the same block-editor component for the experience instance and that works correctly; the itemChanged action defined on the ExperienceController is found.
selectedCurrVitae is outside of the itemController, the itemController is only applied inside the each (on each item, hence the name).
Probably the easiest way to accomplish this will be to reuse the item controller and use render.
Template
{{#if selectedCurrVitae }}
{{ render "cv_index_details" }}
{{/if}}
Controller
CV.CvIndexDetailsController = CV.CurrVitaeController.extend();
Or
Template
{{#if selectedCurrVitae }}
{{ render "currVitae" }}
{{/if}}
View
CV.CurrVitaeView = Ember.View.extend({
templateName: 'cv_index_details'
});

Ember-data how to bind a file upload to model

There are bunch of answer on how to jQuery upload. That's not what I want. I want to simply bind the "file" input so that it's send with my object when I submit the form.
App.Document = DS.Model.extend({
document_name: DS.attr(),
document_file: DS.attr()
});
<form role="form" {{action save on="submit"}}>
<div class="thumbnail" {{action 'start'}}>
<img {{bindAttr src=src}} class="preview"/>
<img class="shadow hide"/>
<canvas class="hide"></canvas>
</div>
{{input type="file" valueBinding="document_file" name="document_file" }}
{{input type="text" valueBinding="document_name" name="document_name"}}
<div>
<button class="btn btn-primary" {{action 'save'}}>Save</button>
</div>
</form>
I haven't found a single tutorial on simple upload. It can't be too hard to send a file right?
It is actually pretty simple to do it althougt it is not functionality out of the box. See my question here with working example: Ember.js value binding with HTML5 file upload
Ember Data doesn't support it out of the box, you'll need to override the adapter and implement your own version of the createRecord/updateRecord which modifies the ajax call. It's probably easier to just use jquery.

Extending Em.TextField

I need to make a bootstrap form with 10 textfield elements.
From the bootstrap doc, I need to have this code for each textfield element:
<div class="control-group">
<label class="control-label" for="inputEmail">Email</label>
<div class="controls">
<input type="text" id="inputEmail" placeholder="Email">
</div>
</div>
Em.TextField object only manage the
<input type="text...
and I would like to minimize my handlebar form.
What is the best solution to have the expected result with the minimum of code ?
Is there a sample code for this ?
You can create a new view that includes all of the required bootstrap markup and incorporates the built in Ember.TextField input element. The custom TextField has an added label property that sets the text for the form label.
JSBin Example
Custom TextField:
JS:
App.BootstrapTextFieldView = Ember.View.extend({
templateName: 'bootstrapTextField',
inputElement: null
});
Handlebars:
<script type="text/x-handlebars" data-template-name='bootstrapTextField'>
<div class='control-group'>
<label class="control-label" {{bindAttr for='view.inputElement.elementId'}}>{{view.label}}</label>
<div class="controls">
{{view Ember.TextField valueBinding='view.value' viewName="inputElement"}}
</div>
</div>
</script>
Using the new view
You would use it similar to using the normal Ember.TextField:
<form class="form-horizontal">
{{view App.BootstrapTextFieldView valueBinding='textFieldValue' labelBinding='textFieldLabel'}}
</form>
The only tricky part was correctly setting the for='..." property on the <label> since we need to get the auto-generated id of the {{view Ember.TextField ...}}. This can be done by using the viewName property of the {{view ...}} helper. Setting {{view App.theViewHere viewName='desiredViewName' lets us access the instance of Ember.TextField we created and set the for property of the label to the view's id using the {{bindAttr...}} helper.

Create reusable Ember.Select control with parameters

I would like to create a specialized select control view, which should simply add some functionality. For simplicity I would just like to be able to add a title.
The Control/View:
window.App.ControlsSelectView = Ember.View.extend({
templateName: 'controls/SelectView',
title:"Hello Control",
contentBinding: null
});
The matching html:
<div class="sub_heading">
<h2>{{title}}</h2>
</div>
<div class="inner_12 input_wrapper custom_select">
{{view Ember.Select
multiple="true"
contentBinding= "contentBinding" // how to pass parameters through?
selectionBinding="content.tempSelectedFunctions"
optionLabelPath="content.displayname"
optionValuePath="content.id"
}}
</div>
Usage should be something like this:
{{ view App.ControlsSelectView
contentBinding="content.data.responsibilities"
selectionBinding="content.tempSelectedFunctions"
title="content.responsibilitTitle"
}}
Problem is, that it doesn't work like that. Is the above possible? Or is there a better pattern to create simple reuasble controls?
This post helped me to get on the right track:
get content in view from ContentBinding
Inside the html of the control view I need to reference the view parameters with view.propertyname
Working solution:
HTML:
<div class="sub_heading">
<h2>{{view.title}}</h2>
</div>
{{#if view.content}}
<div class="inner_12 input_wrapper custom_select">
{{view Ember.Select
multiple="view.multiple"
contentBinding= "view.content"
selectionBinding="view.selection"
optionLabelPathBinding="view.optionLabelPath"
optionValuePathBinding="view.optionValuePath"
}}
</div>
{{/if}}
ControlView
window.App.ControlsSelectView = Ember.View.extend({
templateName: 'controls/SelectView'
});
Usage:
{{view App.ControlsSelectView
title = "Function"
multiple="true"
contentBinding="content.data.functions"
selectionBinding="content.tempSelectedFunctions"
optionLabelPath="content.displayname"
optionValuePath="content.id"
}}