Making an Ember Computed Property depend on a Variable - ember.js

Suppose I have a computed property on a view:
TestView=Ember.View.extend({
computedProperty:function(){
return this.get("elementId")+"test"
}.property("targetObject.{{elementId goes here}}")
})
I want to make the property depend on the model property with the attribute name equivalent to the id on that view element. However, I haven't found an elegant way to do this. I need to do this to make my code as general as possible. My solution so far is to make the property dependent on targetObject's id and then call object.notifyPropertyChange(id) whenever I need binding to update.
To add some clarity. Suppose I have a view that displays a number that is bound to a property on the model. However, I want the number to be formatted with commas when it is displayed. I could easily write a computed property for each possible attribute that will do the formatting. Instead though, I just want a generic view that will know to take the value of the attribute of the model with the same name as the view's elementId property. Here is an example of my hacky solution:
TestView=Ember.View.extend({
computedProperty:function(){
var id=this.get("elementId")
return addCommas(this.get("targetObject").get(id))
}.property("id")
})
I then call:
object.notifyPropertyChange(id) whenever one of the attributes on the model changes. It works and achieves my goals but I was looking for a more elegant solution.
In the view the formatted value is displayed in an custom textfield.
{{view App.CustomTextField value=computedProperty id="someAttribute}}

Just use a helper which formats numbers with commas. You don't need any properties, computed or otherwise.
test.hbs
Hello, I am a formatted {{add-commas number}}.
helpers/add-commas.js
Ember.Handlebars.helper('add-commas', function(value) {
return value.toLocaleString();
});

Related

Ember helper has undefined values

I can't figure out why the helper I created in app/helpers/shop-name.js:
import { helper } from '#ember/component/helper';
export function shopName(shop) {
return shop.identifier + '-' + shop.name;
}
export default helper(shopName);
doe not format the values as needed in my template application.hbs:
<select>
{{#each model as |shop|}}
<option value={{shop.id}}>{{shop-name shop}}</option>
{{/each}}
</select>
I get undefined-undefined displayed in my select list.
If I do the same conversion directly in the template:
<option value={{shop.id}}>{{shop.identifier}}-{{shop.name}}</option>
it works as needed. What's wrong with that helper ?
I'm using Ember 3.0.
Thank you.
I'm not sure that helpers can take inputs that aren't strings. I hope someone can confirm.
Anyway, your use case is really better handled at the model level, using a computed property:
shopName: computed('identifier', 'name', function() {
return `${identifier} - ${name}`;
})
and then you would just use {{shop.shopName}} in your template
There are multiple issues in your code.
shop is an object and I bet it's an Ember.Object. If dealing with Ember.Objects, you should always use get() method to retrieve values for now. This might change soon. Since you are not using get() method, you get undefined. Embers template engine is using getter and therefore it's a huge difference between shop.identifier in your helper and {{shop.identifier}} in your template. Resolve the issue by shop.get('identifier') or import { get } from '#ember/object'; [...] get(shop, 'identifier'); Use the last one if you can't be sure that shop is an instance of Ember.Object.
Since you are passing shop and not shop.identifier as argument to your helper, the helper will only be recomputed if the reference of shop changes (e.g. replaced by another object). Changes to the value of it's properties will not recompute the helper.
One solution for you problem is to use a computed property as suggested by ctcpip. Another one is the concat helper: {{concat shop.identifier '-' shop.name}}.

How to make a NameValueCollection list editable in GlassMapper

I am trying to make a NameValueList collection editable with GlassMapper and I don't seem to be able to get to the bottom of this.
We have a list of validations that can be attached to a field and I would like to have the validation message editable in ExperienceEditor.
The collection is pre-processed when GlassMapper is retrieving the item:
Validations = glassItem.GetValidations();
#foreach(Validation validation in Model.Validations)
{
<div id="#validation.Identifier" ng-message="#validation.AngularKey" ng-cloak class="mtg-validation-msg">
#Html.Glass().Editable(validation, e => e.ErrorMessage)
</div>
}
Error that I am getting:
Failed item resolve - You cannot save a class that does not contain a property that represents the item ID. Ensure that at least one property has been marked to contain the Sitecore ID. Type: MyAssembly.Models.Validation
It is not possible to directly edit certain types of complex fields in the Experience Editor, such as Treelist, Multilist or Name Value Collection.
Instead, you should set up and use an Edit Frame. This will pop up a modal dialog allowing you to edit the field, it is not inline but means you do not need to leave the Experience Editor. This is the recommended approach to this problem.
Since you are using Glass Mapper, since version 4 you can declare Edit Frames all directly from code and now have to declare/set them up in the Core database first.
#if (Sitecore.Context.PageMode.IsExperienceEditor)
{
using (Html.Glass().BeginEditFrame(Model, "Edit", x => x.Validations))
{
<div>Edit Validations</div>
}
}
You might be interested in this blog post I wrote about adding a wrapper around the Edit Frame to make the UX more friendly.

How can I determine if a component returns nothing dynamically?

I have a ember component that is accessing a database and returning the results in a datatable type UI component. I would like to be able to use "N/A" when the result of the component is null or nothing.
For example, I have:
{{each bar in foobars}}
<td class="classyTD">
{{getBars bar=bar}}
</td>
{{/each}}
This works great when I have data, but returns nothing when I don't have data. The designers would prefer an "N/A". Modifying the database isn't an option and while modifying the component getBars is an option, it will be extremely painful.
Is there a method/way to handle this after the execution of the component? If not, or if it's a terrible idea - I'll suffer through changing the component, I trust the community's opinion.
You really should do this inside the component template. You can give the N/A string as a parameter, if that's of any help: http://emberjs.jsbin.com/lemabekuwi/2/edit?html,css,js,output
Or you could change the component that it indicates emptiness through a class and use some css magic: http://emberjs.jsbin.com/duqazahegi/1/edit?html,css,js,output
If you want to limit logic in the handlebars you can have the following in the component js file:
({
setBar: (function() {
if (!this.get('bar')) {
return this.set('bar', 'N/A');
}
}).observes('bar')
});

EmberJS - How to display subset of a model in a view

I am very new (started today) to Ember and cannot figure how how to set this up in a correct manner.
Models:
Post, Comment
On the 'show' template for Post, I want to display only the comments that are not blocked (isBlocked is a attribute of Comment model). Should I use a View and pass in a param to filter out the comments?
I cannot find an useful example or tutorial that explains this. Is there a way similar to how this would be done in Rails with partials and locals or something?
You can use a computed property that uses filterProperty to filter your model inside your controller. Then use that computed property to display in your template.
Assuming your Comment model has an isBlocked attribute, you can set up a computed property like,
comments: function() {
return this.filterProperty('isBlocked', false);
}.property('#each.isBlocked')
Then in the template use comments as the collection to iterate over. The comments collection will have all comments except those where isBlocked is true.

Setting selected entry using helper.options?

Im am using Play 2.0.4 and
helper.options(myitems)
in my template (inside of a helper.select)
In this case, how can I define the default selected entry, which shall be one entry out of myitems? Thanks for any hint!
A little bit more about my case:
Imagine a news archive, showing all news titles. This news archive uses pagination, pagination uses GET to pass the next/previous page number.
The play framework however will only correctly select the currently selected "select" item (here: news category) when a POST request was used - while pagination uses GET!
Intended behaviour: While a filter is applied / a specific news category is selected, this shall always be visible to the user by preselecting the currently selected news category in the "select" form.
A "screenshot" for illustration:
So, anyone having a good idea on how to cope with this problem? Any way to tell Play manually which entry from the "select" form it shall select? '_default always adds a new entry instead of selecting one out of the given options ): Would be great, if one wouldn't have to build the complete "select" form manually.
Try passing '_default option to select helper:
#import views.html.helper._
#select(form("email"), options(List("first", "third")), '_default -> "second")
It seems, unfortunately, the only way to figure it out is to look up the source.
Update:
Specifying _default property doesn't set selected attribute on option tag. It looks like the only way to preselect entry is to pass prefilled form to the template. For example, suppose you have following form:
case class RegInfo(email: String, color: String)
private val registrationForm = Form(
mapping(
"email" → email,
"color" → nonEmptyText(minLength = 5, maxLength = 32)
)(RegInfo.apply)(RegInfo.unapply)
)
Then in the action prefill form before passing to the view:
def create = Action {
Ok(html.create(registrationForm.fill(RegInfo("user#qwe.com", "blue"))))
}
Then in template use helper:
#select(form("color"), options(List("red", "green", "blue")))
And value will be preselected.
Ended up with the pragmatic approach:
<select id="myfield" name="myfield" >
<option class="blank" value="">-- All items --</option>
#for((key, value) <- MyModel.options) {
#if(key == GETValuePassedToTemplate) {
<option value="#key" selected>#value</option>
} else {
<option value="#key">#value</option>
}
}
</select>
Still wondering if there is a better option / way to do it.
Actually, there is a nicer solution to it. If you call the template having the form partially bound you will achieve your goal. Here's the code for your controller:
Ok(views.html.myForm(myForm.bind(
Map("fieldName1" -> "value1",
"fieldName2" -> "value2"))))
Make sure you map fieldnames to the values of the options you want pre-selected.
Still wondering if there is a better option / way to do it.
Well, if your not hell-bent on using play to solve this particular problem, you could always solve it using JavaScript and jQuery:
$(function () {
$('#yourSelect_id').val(5);
}
Where your select options each has values and the one option you whish to pre select has value 5.