I have a number of instances where my Kendo Datasource <-> Backbone connection is working as expected, but I have run into a situation that I hope someone knows more about.
When a model or collection is synced from the server, the datasource is also updated and respective Kendo controls are also updated.
However, if you already have a collection loaded, and you loop through that collection, making a change to the models in that collection, it appears that the linked DataSource is not getting the changes.
Example:
I have a collection of "tags" that are displayed as checkbox options. I want to display some of those checkboxes as "checked" based on other data coming from the server (in this case, I am loading a product - and want to display what colors are selected):
this.colors.each(function (me) {
me.set('selected', '');
if ( _.findWhere(self.model.get('colors'), {id: me.id} ) ) {
me.set('selected', 'checked');
};
})
$('#colors').kendoListView({
dataSource: this.colors_datasource,
template: '<div class="checkbox"><input type="checkbox" value="#: id #" #:selected#> #: name #</div>'
});
The 'this.colors_datasource' is defined in the initialize method of this backbone view. It is not seeing these changes being made to the backbone models in the collection "this.colors". I presume that this is because the models are not syncing to the server.
If I add the definition between these two blocks of code, it works:
this.colors.each(function (me) {
me.set('selected', '');
if ( _.findWhere(self.model.get('colors'), {id: me.id} ) ) {
me.set('selected', 'checked');
};
})
this.colors_datasource = new kendo.Backbone.DataSource({
collection: this.colors
});
$('#colors').kendoListView({
dataSource: this.colors_datasource,
template: '<div class="checkbox"><input type="checkbox" value="#: id #" #:selected#> #: name #</div>'
});
So, my question: Is this the only solution, or is there a way to tell an already defined DataSource to rebuilt itself?
Thanks - StackOverflow Rocks!
I'm guessing there is a timing issue. Perhaps, in the case where it's not working, the datasource is being initialized incorrectly?
The code you provided at the bottom is how you should do it. From the docs you can read the kendo.Backbone.DataSource is providing the magic to get the 2-way binding. Without this the kendo ui does not know about the change events coming from the backbone collection. They provide sample code there that's very similar to what you've suggested here.
I also wanted to comment on this line:
I presume that this is because the models are not syncing to the server.
One of the beautiful things about the Backbone.Events system: it does not require a round-trip to the server in order for the UI to keep in sync with changes. As long as you handle the basic collection events add, remove, change, etc. you can keep your interface up to date without talking to your server.
Related
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.
I started to learn Marionette.View concept.for this I created model with 1 attribute and assign default value.
I have dropdown list in my html file. Whenever it changes it triggers a function name as myfunction.Inside myFunction I changed model attribute value.
Whenever attribute value changes automatically it triggers another function.that event I written inside Marionette.CompositeView.but it's not working.
Earlier I did same thing using myModel.on there it's working fine.But it's not working modelEvents in Marionette.CompositeView.
let's check the following code.
var mymodel=Backbone.Model.extend({
defaults:{
"name":"oldValue"
}
});
var mymodelObj=new mymodel();
//marionette.View code
var backView=Backbone.Marionette.CompositeView.extend({
events:{
"change #pop1":"myFunction"
},
myFunction:function(){
mymodelObj.set({name:"updatedValue"})
},
modelEvents:{
"change:name":"myFunction1"
},
myFunction1:function(){
alert("Hi");
}
});
//creating instance
var backViewObj=new backView({el:$("#sidebar")});
How can I fix this.
Right Now I am trying to understanding where Marionette.js useful in my Backbone Applications.
If I am not mistaken, model is not attached to the view you have created. For modelEvents to be triggered, there should be a model attribute in CompositeView. For this you should specify the model during initialization of CompositeView;
var backViewObj=new backView({el:$("#sidebar"), model : mymodelObj});
To do this though you need to pass the model in when creating the backView like so:
var backViewObj = new backView({ el:$("#sidebar"), model: myModel });
Marionette accomplishes this by overriding delegateEvents which Backbone.View calls in it's constructor and delegating your modelEvents object to the model:
Marionette.bindEntityEvents(this, this.model, Marionette.getOption(this, "modelEvents"));
Based on your comments above I think that you're unclear about the different types of views that Backbone.Marionette provides as you are asking how to pass more model instances. I also was unclear when I began using Marionette.
Marionette.ItemView represents a single model.
Marionette.CollectionView represents a collection and renders a Marionette.ItemView for every model in the collection. It does not have the ability to render a template.
Marionette.CompositeView represents both a model and a collection (leaf and branch in a tree structure). This is for recursive data structures although you can use a CompositeView to render a template (like a table header, etc.) and use itemViewContainer to place the itemViews in a specific element.
If you want your CompositeView to be able to render multiple models you'll need to pass a collection of models into the CompositeView and not one single model:
Does kendo ui support nested Datasources?
I have a table within which I need one of the columns to have a nested table. I am not using json to populate either.
In whatever little documentation I've been able to find it says that nested datasources arnt supported but those use json.
If someone could provide an example of how to implement this, it would be very helpful.
short answer: yes, the HierarchicalDataSource is just am immplementation of the normal datasource that nests. in other words each "node" is a instance of a datasource.
Kendo API Doc
there isn't a ton of documentation of how the HierarchicalDataSource works; I personally had to mess around a lot in the source code to get a handle on it. Kendo only uses it for the treeview and it seems to be built specifically for it. However, You can more or less get it behave how you want by altering the Node model passed into it.
You cannot just use this dataSource with any widget, the widget needs to support it internally. I personally had to create my own listview implemenation to work with it because i wanted nested CRUD .
This is a simple listview implementation that covers a nested template. While is not perfect it seems to be the simplest way to manage this challenge. I would also suggest that the question lacks clarity. I am reaching to answer what I believe to be the obstacle.
// Parent ListView
<div id="parent-listview"></div>
// Parent template
<script id="parent-template" type="text/x-kendo-template">
<a>#=ParentDescription#</a>
// Child ListView
<div id="child-listview-#=Id#"></div>
</script>
// Child template
<script id="child-template" type="text/x-kendo-template">
<a>#=ChildDescription#</a>
</script>
// Bind Parent
$("#parent-listview").kendoListView(
{
template: $("#parent-template").html(),
data : parentData
dataBound: function(e) {
// Bind children
$.each(this.dataItems(), function(idx, item) {
bindChildListView(item);
})
}
});
// Bind each child item
function bindChildListView(data) {
$("#child-listview-" + Id).kendoListView({
template: $("#child-template").html(),
dataSource: data.ChildItems
})
}
NOTE I have a simple int property called Id on my data objects but you can use the uid of the row if you need to or something else.
Using http://www.lukemelia.com/blog/archives/2012/03/10/using-ember-js-with-jquery-ui/ I'm trying to implement a sortable list in a template loaded by an Ember.ArrayController which has an array of Phonenumbers as content :
{{#collection App.SortableListView contentBinding="phonenumbers" itemViewClass="App.SortableItem"}}
<b>{{phonenumber.cost}}</b>
{{/collection}}
I'm trying to update the 'order' property of each phonenumber after a sort but I can't figure out how to do that :
App.SortableListView = JQ.SortableListView.extend({
items: 'article',
//update is a jquery ui method called when the sort ends and dom has been updated
update: function(event, ui) {
// I would like to update and persist each phonenumbers model here
}
});
I have tried various things : bindAttr on order, data attributes through jquery, using the contentIndex property of each childView (but it's not updated when sorting)... Everything failed, or was too much complexity for such a simple thing.
I guess I'm missing something obvious, thx for your help.
There is sortProperty method on array controller that do what you want
I have a big problem with using Knockout JS. In my view model I have a field, called Method, that is actually an other view model.
This view model can be one of three different things (it is mapped to a polymorphic object in the domain model). To solve this I use templates that checks which type of Method that is selected withing the domain model and then shows the template that binds data for that type.
The function that checks the type of method looks like:
this.getTemplate = function (data) {
var method = data.original.get_Method();
if (method instanceof MyProj.MethodA)
return "methodA";
else if (method instanceof MyProj.MethodB)
return "methodB";
else if (method instanceof MyProj.MethodC)
return "methodC";
}
The markup where I bind the template looks like:
<div data-bind="template: {name: getTemplate($data), data: $data.Method}"></div>
This actually works very nice and when I change the type of method via an dropdown in the UI the domain model updates and the right template is shown. However here comes my problem. Each template contains a number of different fields that are specific for each method type. Whenever I change one of the values in the view model displayed by one of the templates the UI flashes and I think that happens because the template get selected again. This is quite irritating and looks extremly bad.
Any ideas on how to solve this problem? Any help would be greatly appreciated!
Thanks in advance
/Björn
Did you use any observable inside the getTemplate function. Updating the value of that observable makes the template rerender and you get your flash effect.
Checkout this link Part : "Note 5: Dynamically choosing which template is used".