Update Ember Handlebar template when if/else condition changed - ember.js

I'm using a little "helper" function in my app to get the current user/login status:
App.ApplicationController = Ember.Controller.extend({
isAuthenticated: function() {
return Docket.AuthManager.isAuthenticated()
}.property('Docket.AuthManager.apiKey'),
currentUser: function() {
return Docket.AuthManager.get('apiKey.user')
}.property('Docket.AuthManager.apiKey')
});
Now this is how my application.hbs looks:
{{#if isAuthenticated}}
foo
{{else}}
bar
{{/if}}
But even if isAuthenticated return another value, the template doesn't get it. Only solution: refreshing the page. How can I achieve that without refreshing the whole page?

if Docket.AuthManager.apiKey isn't an ember property, (which I'm pretty sure it isn't) ember won't know when it's changed causing the computed property to re-trigger and check again. Ember is only aware of properties that are get/set using its getter and setters.

Related

Ember.JS data model: Filtering a computed property

I have an Ember data model logger defined as below:
import DS from 'ember-data';
import EmberObject, { computed } from '#ember/object';
export default DS.Model.extend({
someAttribute: DS.hasMany('attr'),
test: computed('someAttribute.[]', function(){
return this.get('someAttribute').filterBy('description', 'some value');
})
});
The above model gets passed as logger variable from the controller into my component template. In my template:
{{#if logger.test}}
<h1> Testing </h1>
{{log logger.test.description }}
{{/if}}
It seems like the logger.test in the template is always false. In the same template if I add the following:
{{#each logger.someAttribute as |t|}}
{{t.description}}
{{/each}}
I can see all the values being enumerated. Not sure what I am missing? Any assistance would be greatly appreciated.
Okay, I figured out. Turns out models return promises and the if statement doesn't handle promise well enough. The right way to do this would be to return a DS.promiseArray from the computed property and then everything works like a charm:
return DS.PromiseArray.create({
promise: this.get('someAttribute').then(logs => {return logs.filterBy('description')})
});
Acknowledgements: https://emberigniter.com/guide-promises-computed-properties/
I don't exactly understand what you're trying to achieve, but I would either
Load the DS.hasMany('thing', {async: false}) and make sure they are included in the store. (See https://embermap.github.io/ember-data-storefront/latest/). If the relationship is set to async: false when it's accessed, it's accessed synchronously, so there is no issues with promises.
Using ember-concurrency can help manage the loading of the records and displaying them on the page.

Why doesn't the handlebars "if" helper call the actual model computed property?

I'm maintaining an application in Ember, and this "computed property" never actually gets computed by the if block helper in the template.
Model:
var item = App.Item.create({
isAddedToCart: function () {
console.log('addedToCart method has been called');
return false;
}.property()
});
Template:
{{#if item.isAddedToCart}}
{{log 'true'}}
{{else}}
{{log 'false}}
{{/if}}
In my component that is in the template, the console.log() never gets called, but the function seems to be tested if it's truth-y or not. Is there something wrong in my approach?
Edit: The application is written in Ember version 1.7 (I know...)
Which versions you are currently using, actually i tried what you did in ember version 1.12.2 and I got the following assertions which clearly explains how to solve it.
Error while processing route: index Assertion Failed: Ember.Object.create no longer supports defining computed properties. Define computed properties using extend() or reopen() before calling create(). Error: Assertion Failed: Ember.Object.create no longer supports defining computed properties. Define computed properties using extend() or reopen() before calling create().
I created sample twiddle to demonstrate the usage,
controllers/application.js
import Ember from 'ember';
var App.Item = Ember.Object.extend({
});
App.Item.reopen({
isAddedToCart: function () {
console.log('addedToCart method has been called');
return false;
}.property()
});
export default Ember.Controller.extend({
appName: 'Ember Twiddle ',
itemObj : App.Item.create(),
});
and in application.hbs,
{{itemObj.isAddedToCart}}
May be for earlier version if the ember allows what you did, there might be below problem,
- ensure item property is accessible in template
- check for overriding any place or manual set for isAddedToCart

Ember 2.3 how to use itemControllers in each loops?

Before anyone brings up components, I must state that I am aware that Ember is moving away from controllers and views completely and adopting the component structure. Right now, I am compelled to use controller/view in ember2.3 using the legacy-controller and legacy-view addons that have been provided here:
https://github.com/emberjs/ember-legacy-controllers
https://github.com/emberjs/ember-legacy-views
as part of the process to upgrade to Ember 2.3 (from 1.7).
Now, I have a route called recordTypes, which lists all recordTypes. in the legacy code, each recordType was then associated with an itemController 'recordType'. Like so:
{{#each result in searchResults itemController="recordType"}}
...
{{/each}}
Surprisingly, this legacy syntax for Ember did not render anything to the page, but the following one did:
{{#each searchResults itemController="recordType" as |result| }}
...
{{/each}}
The itemController recordType is a legacy Object Controller and the recordTypes controller itself is a legacy Array Controller.
Now, for each result I have a few actions that can be performed. For example, on clicking the result, the editResultName action was to be fired. This action, in the legacy code, was in the recordType controller. Therefore, clicking the item in the recordTypes page would then defer this action to the recordType controller, which would then happily handle the rest.
This is not being fired in ember2.3, even with the legacy controllers. What surprises me more is that this code can be found in ember-legacy-controller.js
export default {
name: 'ember-legacy-controllers',
initialize: function() {
/**
Adds support for ArrayController in the legacy {{each}} helper
*/
Ember._LegacyEachView.reopen({
_arrayController: computed(function() {
var itemController = this.getAttr('itemController');
var controller = get(this, 'container').lookupFactory('controller:array').create({
_isVirtual: true,
parentController: get(this, 'controller'),
itemController: itemController,
target: get(this, 'controller'),
_eachView: this,
content: this.getAttr('content')
});
return controller;
}),
_willUpdate(attrs) {
let itemController = this.getAttrFor(attrs, 'itemController');
if (itemController) {
let arrayController = get(this, '_arrayController');
set(arrayController, 'content', this.getAttrFor(attrs, 'content'));
}
}
});
}
};
Here, it does have a line that references the itemController. However, when this list of searchResults is rendered, and a result is clicked, the error I get is this:
Nothing handled the action 'editResultName'. If you did handle the action, this error can be caused by returning true from an action handler in a controller, causing the action to bubble.
The action is there for sure, but nothing in the itemController is being recognised. Unfortunately a lot of the legacy code I am updating has itemController loops and therefore it would be immensely helpful to be able to use itemController for the time being.
How can I use itemController like it used to be implemented?
Replacing an itemController.
Create a component from the contents inside the each helper. The itemController would become the js side of the component and the template code the template
From this:
{{#each result in searchResults itemController="recordType"}}
<span>result: {{result.title}}</span>
{{/each}}
To this:
{{#each searchResults as |result| }}
{{result-list-item result=result}}
{{/each}}

Ember component: computed property observing local storage array

I'm somewhat at a loss with what should be easily achieved with Ember computed properties. I'm using https://github.com/funkensturm/ember-local-storage to manage a local favorites list.
Now I have a component which should display the object's status, along with the infamous "star" button for toggling the favorite state.
My component code goes as follows:
import Ember from 'ember';
import FavoritesLocal from 'my-app/models/favorites-local';
export default Ember.Component.extend({
storedFavorites: FavoritesLocal.create(),
isFavorite: Ember.computed('storedFavorites', function () {
return this.get('storedFavorites').contains(this.model.get('id'));
}),
actions: {
toggleFavorite() {
if(this.get('isFavorite')) {
this.get('storedFavorites').removeObject(this.model.get('id'));
} else {
this.get('storedFavorites').addObject(this.model.get('id'));
}
}
}
});
The template contains a
{{#if isFavorite}}
<a {{action 'toggleFavorite'}} href="#"></a>
{{else}}
<a {{action 'toggleFavorite'}} href="#"></a><
{{/if}}
The model for the local-storage is simply
import StorageArray from 'ember-local-storage/local/array'
export default StorageArray.extend({
storageKey: 'myFavorites'
});
Now, of course I want the component to update if the button is clicked.
My specific question is concerning WHAT exactly the computed property should be observing. A crude attempt for listening for changes in the storedFavorites property (see above) failed.
Clearly, I could send an action up to the controller and let it handle it and update the template, but that seems a little overdo? What am I missing?
Thanks!
I haven't worked with this yet, but my guess is that you want to observe storedFavorites.length.

Adding component dynamically in Ember

using
Ember : 1.13.11,
Ember Data : 1.13.8,
ember-cli : 1.13.12
I want to add a component dynamically to webpage - this webpage is template of another component don't think that it will make any difference-. Here is my code snippet in which I try to add a component named LyricsEditorLine to <div> tag, somehow like this
agenda-alpha/components/lyrics-editor.js
import Ember from 'ember';
import LyricsEditorLine from 'agenda-alpha/components/lyrics-editor-line';
export default Ember.Component.extend({
afterRenderEvent:function(){
LyricsEditorLine.create().appendTo($("#in"));
},
init:function(){
Ember.run.scheduleOnce('afterRender', this, this.afterRenderEvent);
this._super();
}
});
agenda-alpha/templates/components/lyrics-editor.hbs
<div id='in'> </div>
every time this gives me
'Uncaught Error: Assertion Failed: You cannot append to an existing Ember.View. Consider using Ember.ContainerView instead'
Looked for ContainerViewhere found that it is deprecated
Most of the answers that I found are not using ember-cli and being a beginner makes it harder to understand
I want to be able to add components as much as the user needs
I think you probably want the {{component}} helper which allows to dynamically render a component.
{{component "componentName" param1=paramValue param2=anotherParamValue}}
Which means you can have (made up example)
{{component "lyrics-editor-line" line=line}}
One the best things is that componentName can be a bound property
{{component componentName line=line}}
And in your controller/component:
componentName: Ember.computed('prop1','prop2', function() {
if (this.get('prop1') === 'A') {
return 'my-component-a';
}
return 'default-component';
}),
line: computed('prop3', function() {
return this.get('prop2');
})
Also, you can have the component helper inside an each loop (example taken from the Ember documentation)
{{#each model as |post|}}
{{!-- either foo-component or bar-component --}}
{{component post.componentName post=post}}
{{/each}}