Unwanted two-way data binding when passing data to Ember Octane component - ember.js

I'm following the Full Stack Ember with Rails course on Embercasts. As they don't use Octane yet, I change the code where necessary to work with Ember 3.22. I'm stuck in video 22 where I need to pass data to a component. This data should only be passed to the component, but when updating a value inside it, I don't want to see the change in other places.
In the video, this is taken care of in a didReceiveAttrs() handler, but this is not available in Octane. Instead, the Ember docs describe one way data flow as what happens automatically. In my case, it doesn't:
{{! author.edit.hbs !}}
<h3>Editing: {{model.last}}, {{model.first}}</h3>
<AuthorForm #author={{model}} />
{{! author-form.hbs !}}
<div class="field">
<label for="first">First name</label>
<Input #id="first" type="text" placeholder="First name" #value={{this.author.first}}/>
</div>
<div class="field">
<label for="last">Last name</label>
<Input #id="last" type="text" placeholder="Last name" #value={{this.author.last}}/>
</div>
The h3 updates whenever I change the value in one of the component's inputs. What's wrong here?

The <Input> component shipped with Ember.js uses two-way data binding for the value.
Arguments passed to a component are immutable. You can not change this.args nor a value passed with #. But object passed in as values of arguments are not frozen.
Taking this template as an example:
<Input #value={{#post.title}}/>
It will not mutate #post but the title property of the object passed in as #post argument.

Related

Changing property values from within the component in Ember

I'm wondering how I'd go about dynamically editing a component property from within the component. Code might help you get a bit more clarity on what I'm trying to do.
Templates/boards.hbs
<div>
{{board-component title='Title that wants to be dynamic'}}
</div>
Components/board-component.hbs
{{#if isEditing}}
<div>
<input type="text" value={{title}}>
</div>
{{else}}
<div>
{{title}}
</div>
{{/if}}
Am I right in saying that standard behaviour would have the value I specify in the input reflect as the title, but due to the fact that I've declared the value in the template it reverts back to this declared value?
How can I get around this?
<input type="text" value={{title}}>
It means, board-component title property will get values from boards.hbs. and initially, this will be displayed in the input. but changing input value will not reflect it in title property of the components.
But if you used input helper like below,
{{input type="text" value=title}}
It is two way binding between input value and title property. so whenever you change value from input that will reflect in components.
So answer to your question, use input helper.
{{input type="text" value=title}}

How to get an EmberJs checkbox to send an action with multiple parameters

How do you get a checkbox using the {{input}} helper in EmberJs to send an action with multiple parameters?
Here is the current template:
<input
type="checkbox"
class="t-dtd-field-checkbox"
checked="checked"
{{action "updateRequest" option field ticket}}
>
I'd like to do something like this:
{{input
type="checkbox"
class="t-dtd-field-checkbox"
checked={{option.checked}}
{{action "updateRequest" option field ticket}}
}}
However, this is causing a syntax error, and not sure how to get the syntax correct?
Also, how do you get the checkbox's value to update in the DOM on keypress?
Thanks in advance!
Take a look at the documentation here (under actions):
https://guides.emberjs.com/v2.5.0/templates/input-helpers/
You will find your answer here:
https://guides.emberjs.com/v2.5.0/tutorial/autocomplete-component/
{{input
type="checkbox"
class="t-dtd-field-checkbox"
checked=option.checked
on-click=(action "updateRequest" option field ticket)
}}
Hope this helps

Input helper valueBinding is deprecated - what's the alternative?

I've got a few text-input helper like this
{{input type="text" valueBinding="name" focus-out="focusOutName"}}
I just upgraded Ember to 1.11.0 and now get this deprecation warning:
DEPRECATION: You're attempting to render a view by passing valueBinding to a view helper, but this syntax is deprecated. You should use value=someValue instead.
However when using value it is not bound in the controller and value simply sets the text to whatever value.
How do I correctly bind it?
You should just have to change:
{{input type="text" valueBinding="name" focus-out="focusOutName"}}
to:
{{input type="text" value=name focus-out="focusOutName"}}
or even better (don't need the type="text", it's automatic):
{{input value=model.name focus-out="focusOutName"}}
then next to it you can display the value, and see it change when you change the input (so you can test for yourself that the bindings are set up already):
{{input value=model.name focus-out="focusOutName"}}
{{model.name}}

Transition from {{view Ember.TextField}} to {{input type="text"}}

I am upgrading from Ember 1.7 to Ember 1.8.0-beta.2 (incremental upgrade, I hope to reach the latest 1.8.0-beta.4 without much trouble).
Apparently, Ember.TextField is deprecated:
DEPRECATION: Resolved the view "Ember.TextField" on the global
context. Pass a view name to be looked up on the container instead,
such as {{view "select"}}.
http://emberjs.com/guides/deprecations#toc_global-lookup-of-views-since-1-8
This is my original implementation:
{{view Ember.TextField classNames="form-control" valueBinding="properties§name" id="name-id" placeholderTranslation="generic.name" required="true"}}
So I have tried with (as I have done with other views):
{{view "textField" ...}}
No luck:
Uncaught Error: Assertion Failed: textField must be a subclass of
Ember.View, not
So, it seems it is now a component. So I try with:
{{input type="text" classNames="form-control" value=properties§name id="name-id" placeholderTranslation="generic.name" required="true"}}
And it seems to work, but I am worried that I am maybe doing something wrong, because neither id nor classNames nor placeholderTranslation are listed as supported properties for the input component.
So here my questions:
am I right in doing this refactoring {{view Ember.TextField}} -> {{input type="text"}}?
what about the properties not explicitly supported? They seem to work in my case. Is this a problem of outdated documentation, or ...
is there a list of generic properties supported by all components? I can not find any.
About your questions on ember here is my answer step by step:
Yes you are doing right refactoring
OLD- {{view Ember.TextField}}
NEW- {{input type="text" value="" name="" class=""}}
"Class" etc properties are anyways supported by html input so when you will add class, placeholder etc things those will be automatically supported as per my understanding
Also all property which are mentioned in Ember Doc other than that whatever is supported in html will always work. You can read the Comment mentioned in ember Ember source-code
{{input type="text" value="111" name="mytest" class="icon" id="wow"}}
AND I got
<input id="wow" class="ember-view ember-text-field icon" type="text"
name="mytest" value="111">

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.