Ember.js: getting model relationships from component's javascript - javascript

I am building a component, and I have noticed a behaviour that seems very odd to me. My component is invoked as follows:
{{my-component model=model}}
My model contains a relationship as follows:
type: DS.belongsTo('type')
Now, in my-component.js, if I log to console this.get('model.type.name') (or this.get('model').get('type').get('name')), I get undefined. However, if in my-component.hbs I insert {{model.type.name}}, the value is displayed correctly.
I don't really understand this behaviour: how can I access a model's relationship from within a component's javascript just like I do in the component's Handlebars template?
Thanks!

In ember-data, relationship is treated as Promise so you should use then for the result.
this.get('model').get('type').then((result) =>{
console.log(' Name ', result.get('name'));
});
Refer:
https://guides.emberjs.com/v2.14.0/models/relationships/#toc_relationships-as-promises

Related

Ember.js computed property to return model ID array?

Updated: 11:36PST 07 Dec 2017
All I'm trying to do is create an array containing a list of current ID's in the model to use in a child component, but somehow seem to be missing something obvious. If it is relevant, I am using Ember 2.17.0 with Ember Data 2.17.0 as well.
The route returns an array of models very similar to a findAll, but modified to work with a REST endpoint I do not have any control over. I need an array of the id's from the model to use in a component embedded in the route. Based on feedback, I have attempted to implement this in both the route and the controller.
In the controller, I'm trying it this way.
searchIdArrayC: computed('model', () => {
return this.get('model').map((record) => record.get('reachId'));
})
..and in the route I'm trying it this way.
searchIdArrayR: computed('model', () => {
return this.modelFor('reaches').map((record) => record.get('reachId'));
});
When I look at it in the Chrome Ember Inspector, for both the route and controller it is telling me Error while computing: searchIdArrayR or Error while computing: searchIdArrayC.
Just to try and do some testing, I sent both the route and the controller to the console. With both the route and controller, the aforementioned methods are working, so I am really confused.
Thank you in advance for any help or guidance you may be able to offer.
In route file, model is a function, but you are treating it like model property which is available in the corresponding controller.
If you can move searchIdArray computed property to corresponding controller then that should work.

Ember.js: accessing computed alias from component's javascript

I currently have a model that contains a computed alias, as follows:
model: DS.belongsTo('device-model'),
manufacturerName: Ember.computed.alias('model.manufacturer.name')
I then have a component that is invoked as follows:
{{my-component model=model}}
Now, in the component's Handlebars template, I can easily access the computed property with {{model.manufacturerName}}, however in my-component.js I am having trouble getting it to work. I have tried with:
console.log(this.get('model').get('manufacturerName'))
However, the output is undefined. So far, the only way I can get the manufacturer name from my-component.js is:
this.get('model').get('model')
.then((model) =>{
return model.get('manufacturer')
})
.then((manufacturer) => {
console.log(manufacturer.get('name'))
})
So, I'm wondering what is the Handlebars template doing that I can't do in its javascript counterpart? It seems like the Handlebars template is following through the promise, whereas I have to do it manually when it comes to the component's javascript.
Thank you!
I think the issue is because of your belongsTo relationship. since { async: true} is the default value for relationships in Ember. so it only fetch the related entities when you actually request them. which means your model is not loaded which means your manufacturerName is not loaded since it is an alias of model.manufacturerName.name.

Reading OData contexts in onInit of controller

I've tried to prepare data from an OData source to show it in a bar graph in my fiori app. For this, I setup the OData model in the manifest.json. A test with a list, simply using
items="{path : 'modelname>/dataset'}
works fine and shows the content.
To prepare data for a diagram (VizFrame), I used the onInit() function in the controller of the view (mvc:XMLView). The data preparation is similar to the one discussed in question.
At first I obtain the ODataModel:
var oODataModel = this.getOwnerComponent().getModel("modelname");
Next I do the binding:
var oBindings = oODataModel.bindList("/dataset");
Unfortunately, the oBindings().getContexts() array is always empty, and also oBindings.getLength() is zero. As a consequence, the VizFrame shows only "No Data".
May it be that the data model is not fully loaded during the onInit() function, or do I misunderstand the way to access data?
Thanks in advance
Update
I temporary solved the problem by using the automatically created bind from the view displaying the data as list. I grep the "dataReceived" event from the binding getView().byId("myList").getBindings("items") and do my calculation there. The model for the diagram (since it is used in a different view) is created in the Component.js, and registered in the Core sap.ui.getCore().setModel("graphModel").
I think this solution is dirty, because the graph data depends on the list data from a different view, which causes problems, e.g. when you use a growing list (because the data in the binding gets updated and a different range is selected from the odata model).
Any suggestions, how I can get the odata model entries without depending on a different list?
The following image outlines the lifecycle of your UI5 application.
Important are the steps which are highlighted with a red circle. Basically, in your onInit you don't have full access to your model via this.getView().getModel().
That's probably why you tried using this.getOwnerComponent().getModel(). This gives you access to the model, but it's not bound to the view yet so you don't get any contexts.
Similarly metadataLoaded() returns a Promise that is fullfilled a little too early: Right after the metadata has been loaded, which might be before any view binding has been done.
What I usually do is
use onBeforeRendering
This is the lifecycle hook that gets called right after onInit. The view and its models exist, but they are not yet shown to the user. Good possibility to do stuff with your model.
use onRouteMatched
This is not really a lifecycle hook but an event handler which can be bound to the router object of your app. Since you define the event handler in your onInit it will be called later (but not too late) and you can then do your desired stuff. This obviously works only if you've set up routing.
You'll have to wait until the models metadata has been loaded. Try this:
onInit: function() {
var oBindings;
var oODataModel = this.getComponent().getModel("modelname");
oODataModel.metadataLoaded().then(function() {
oBindings = oODataModel.bindList("/dataset");
}.bind(this));
},
May it be that the data model is not fully loaded during the onInit()
function, or do I misunderstand the way to access data?
You could test if your model is fully loaded by console log it before you do the list binding
console.log(oODataModel);
var oBindings = oODataModel.bindList("/dataset");
If your model contains no data, then that's the problem.
My basic misunderstanding was to force the use of the bindings. This seems to work only with UI elements, which organize the data handling. I switched to
oODataModel.read("/dataset", {success: function(oEvent) {
// do all my calculations on the oEvent.results array
// write result into graphModel
}
});
This whole calculation is in a function attached to the requestSent event of the graphModel, which is set as model for the VizFrame in the onBeforeRendering part of the view/controller.

Can RefluxJS stores indicate which property has changed when they call trigger()?

I'm new to Flux as a whole, but I'm trying to get a grip on it by starting with Reflux, which seems a bit more opinionated and simpler to learn.
As I understand, Reflux stores have a trigger method which indicates the store's data has changed, and they pass the updated data into it. This data can then be set as a React component's state, (or as one of the state's properties) using the Reflux.connect mixin or similar methods.
But what if a store has multiple sets of data that need to be listened to separately? Let's say I'm modifying the TodoMVC RefluxJS example, and I wanted the TodoStore to also include a title property that indicated the name of the todo list (as well as the list, the list of TODO items). Lets say there is also a <Title> component that is listening for changes to the title property, and setting the title as its state when it does.
A call to this.trigger(title) would update the title component, but would also cause the todo component to try to use the title string as its state, so we need a way to indicate which data has been changed. Should these two properties (title and list) be separated into different stores? Or should all calls to trigger include a string that indicates the property: this.trigger("title", this.title) or this.trigger("todos", this.list). Or should all the data be combined into one object which is then picked by the listeners (e.g. using Reflux.connectFilter)?
this.trigger("todos", {
todos: this.list,
title: this.title
});
These last two examples introduce new data to the this.trigger() call, meaning that Reflux.connect can't be used any more, because connect takes the data returned from a store and directly sets the components state to it. Does this mean we have to use Reflux.listenTo(TodoStore,"onTodoChange"), and then filter out the trigger calls that aren't relevant to this component?
(1) Its very important stores broadcast data change event to the subscribed top level view components.(The so-called controller views, as explained in http://facebook.github.io/flux/docs/overview.html).
(2) The re-usable components, such as List, Title etc,etc. are self complete, these components should not understand store data structure. Use properties instead of setState for display data.
(3) Do you really want the store to hold different type of data, or does the data belong to a different store.
(4) If the store must hold different type of data, my preference is not to "filter" by action type. Update all the view components listening to the store for simplicity.

reverse order Ember model collection inside component?

I'm pulling model data inside my IndexRoute by using:
http://pastebin.com/pf1mBgTU
And I have a component that's being passed the notifications.
I need to be able to reverse the order of those notifications though.
The problem is that since I'm pulling multiple models into the IndexRoute - I can't use sortProperties inside of the IndexController.
Anyone know how to sort these either before they get sent to the component, or from inside the component itself?
Thanks!
You should be able to use the controller's Sortable mixin to do this for you...
App.IndexController= Ember.ArrayController.extend({
sortFunction(x,y) {
// here check for model type and make comparison between x and y
}
});
Set your sort property to something that will have your type information, like a meta section. If you don't have such an identifier, you may need to instead implement orderBy.

Categories