How to bind a Kendo UI list to a ViewModel? - javascript

I have this fallowing list:
<ul data-template="view" data-bind="source: contacts"></ul>
...
<script type="text/x-kendo-template" id="view">
<li>
<div data-role="button" data-bind="click: viewContact">
<h4>#: first_name #</h4>
</div>
</li>
</script>
var someClass = kendo.observable({
title : 'Contact',
contacts : [],
getContacts: function () {
var data = new Contacts().get();
$.when(data()).then(function (data) {
someClass.contacts = data;
});
}
});
I want to assign data from getContacts to products and populate the template with results as they come in.
Any ideas?

In your question you are talking about "products" but in the code snippet i see "contacts".
In any case, you need to do the following:
someClass.set('contacts', data)
Note: assuming you have contacts as a property on the viewmodel.
Since its an observable object, if you don't use get() & set() methods, kendo wont know who all are observing that property. If you use set() method to set new value, Kendo will propagate the change and will notify the listeners of the property.

Related

Vue JS - Putting Json on data

I want to put my JSON data into Vue data, and a display, why can't I get to work?
compiled: function(){
var self = this;
console.log('teste');
$.ajax({
url: 'js/fake-ws.json',
complete: function (data) {
self.$data.musics = data;
console.log(self.$data.musics);
}
})
}
<div id="playlist" class="panel panel-default">
<div class="panel-body">
<ul>
<li v-repeat="musics.item" >
{{nome}}
</li>
<ul>
<div>
</div>
I can't get the code to work.. why?
I think the problem is that musics is not initially part of your Vue data, so when you set its value using self.$data.musics = data, Vue doesn't know it needs to watch it. Instead you need to use the $add method like this:
self.$set("musics", data);
From the VueJs Guide:
In ECMAScript 5 there is no way to detect when a new property is added to an Object, or when a property is deleted from an Object. To deal with that, observed objects will be augmented with two methods: $add(key, value) and $delete(key). These methods can be used to add / delete properties from observed objects while triggering the desired View updates.
this refers to the whole Vue object, so musics object is already accessible via this.musics. More info here in the VueJS API reference and here in the VueJS guide, and more on this here.
With that in mind the code should look something like this:
var playlist = new Vue({
el: '#playlist',
data:{
musics: '',
}
methods: {
compiled: function(){
var self = this;
console.log('test');
$.ajax({
url: 'js/fake-ws.json',
complete: function (data) {
self.musics = data
console.log(self.musics);
}
})
}
}
And the view would be something like this:
<div id="playlist" class="panel panel-default">
<div class="panel-body">
<ul>
<li v-repeat="musics">
{{nome}}
</li>
<ul>
</div>
</div>
Also look at the code of this example.
you can do that with vue-resource. Include vue-resource.js into your app or html file and:
{
// GET /someUrl
this.$http.get('/someUrl').then(response => {
// get body data
this.someData = response.body;
}, response => {
// error callback
});
}

KnockoutJS, how to update view model using HTML5 content editable?

I have the following code:
HTML:
<ul class="list" data-bind="foreach: list">
<li class="title" data-bind="text:title" contenteditable="true"></li>
<li class="item" data-bind="text:item" contenteditable="true"></li>
</ul>
<button type="button">Save</button>
JS:
var data = {
"list": [{
"title" : "title one",
"item" : "item one"
}]
}
var viewModel=
{
list : ko.observable(data.list)
};
ko.applyBindings(viewModel);
$("button").on("click", function(){
var vm = viewModel;
ko.applyBindings(vm);
var data = ko.toJSON(vm);
console.log(data);
});
However when I do this I get this error:
Uncaught Error: You cannot apply bindings multiple times to the same element.
knockout-3.1.0.js:58
What I would like to do is change the text of one of the items and have it save to the view model when I click the save button.
FIDDLE:
http://jsfiddle.net/sr4Fg/13/
There are few things.
only call applyBindings once, then ko will sync data for you.
you don't need to create a save button, ko sync data automatically.
your title and item are NOT ko.observable, hence ko has no way to auto-update it for you.
ko "text" binding is kind of one way binding, it only updates view when your value changes. You need to use "value" binding in an input tag to get two way binding.
right now, there is no existing ko binding supporting contenteditable. You may build a custom bindingHandler for it, but beware it's tricky to get contenteditable change event.
you list should be an observableArray.
Here is the working example with "value" binding: http://jsfiddle.net/sr4Fg/41/
<ul class="list" data-bind="foreach: list">
<li class="title"><input data-bind="value:title" /></li>
<li class="item"><input data-bind="value:item" /></li>
</ul>
<button type="button">Save</button>
var viewModel=
{
list : ko.observableArray([{
"title" : ko.observable("title one"),
"item" : ko.observable("item one")
}])
};
ko.applyBindings(viewModel);
$("button").on("click", function(){
var data = ko.toJSON(viewModel);
console.log(data);
});
Understand you may load JSON data from ajax call, it's tedious to change all the values into ko.observable. Try http://knockoutjs.com/documentation/plugins-mapping.html if you need.

ember.js apply object after setting new value for it's attribute

Cheers!
Simple action here I have:
TravelClient.TourSeatsController = Ember.ObjectController.extend({
selectSeat: function(seat) {
// console.log(seat.get('id'));
// console.log(seat.get('free'));
seat.set('free', false);
TravelClient.Seat.apply(seat);
// console.log(seat.get('free'));
}
});
my html:
{{#each seat in controller.seatsArray}}
<li class="item">
{{#if seat.free}}
<div class="counter-value">
<a href="#" {{action "selectSeat" seat}}>{{seat.id}}</a>
</div>
...............................................................................
Object's structure:
...addObject(TravelClient.Seat.create({
id: i,
free: true
}));
Set method works well, but when I trying to apply object with new setted property, Ember gives me an error:
Uncaught TypeError: Cannot redefine property: __ember1360328485839
How to fix this?
Try creating a transaction, add the seat to the transaction, modify the seat, then commit the transaction.
TravelClient.TourSeatsController = Ember.ObjectController.extend({
selectSeat: function(seat) {
var transaction = new App.store.transaction();
transaction.add(seat);
seat.set('free', false);
transaction.commit();
}
});

Passing values to ko.computed in Knockout JS

I've been working for a bit with MVC4 SPA, with knockoutJs,
My problem is I want to pass a value to a ko.computed. Here is my code.
<div data-bind="foreach: firms">
<fieldset>
<legend><span data-bind="text: Name"></span></legend>
<div data-bind="foreach: $parent.getClients">
<p>
<span data-bind="text: Name"></span>
</p>
</div>
</fieldset>
</div>
self.getClients = ko.computed(function (Id) {
var filter = Id;
return ko.utils.arrayFilter(self.Clients(), function (item) {
var fId = item.FirmId();
return (fId === filter);
});
});
I simply want to display the Firmname as a header, then show the clients below it.
The function is being called, but Id is undefined (I've tried with 'Firm' as well), if I change:
var filter = id; TO var filter = 1;
It works fine,
So... How do you pass a value to a ko.computed? It doesn't need to be the Id, it can also be the Firm object etc.
Each firm really should be containing a list of clients, but you could use a regular function I think and pass it the firm:
self.getClientsForFirm = function (firm) {
return ko.utils.arrayFilter(self.Clients(), function (item) {
var fId = item.FirmId();
return (fId === firm.Id());
});
});
Then in html, $data is the current model, in your case the firm:
<div data-bind="foreach: $root.getClientsForFirm($data)">
Knockout doesn't allow you pass anything to a computed function. That is not what it is for. You could instead just use a regular function there if you'd like.
Another option is to have the data already in the dataset on which you did the first foreach. This way, you don't use $parent.getClients, but more like $data.clients.

How to bind click handlers to templates in knockoutjs without having a global viewModel?

I'm very new to KnockoutJs so I'm hoping that there is a well known best practice for this kind of situation that I just haven't been able to find.
I have a view model that contains an array of items. I want to display these items using a template. I also want each item to to be able to toggle between view and edit modes in place. I think what fits best with Knockout is to create the relevant function on either the main view model or (probably better) on each item in the array and then bind this function in the template. So I have created this code on my page:
<ul data-bind="template: {name: testTemplate, foreach: items}"></ul>
<script id="testTemplate" type="text/x-jquery-tmpl">
<li>
<img src="icon.png" data-bind="click: displayEditView" />
<span data-bind="text: GBPAmount"></span>
<input type="text" data-bind="value: GBPAmount" />
</li>
</script>
<script>
(function() {
var viewModel = new TestViewModel(myItems);
ko.applyBindings(viewModel);
})();
</script>
And this in a separate file:
function TestViewModel(itemsJson) {
this.items = ko.mapping.fromJS(itemsJson);
for(i = 0; i < this.items.length; ++i) {
this.items[i].displayEditView = function () {
alert("payment function called");
}
}
this.displayEditView = function () {
alert("viewmodel function called");
}
};
Due to the environment my JS is running in I can't add anything to the global namespace, hence the annonymous function to create and set up the view model. (There is a namespace that I can add things to if it is necessary.) This restriction seems to break all the examples I've found, which seem to rely on a global viewModel variable.
P.S. If there's an approach that fits better with knockoutJS than what I am trying to do please feel free to suggest it!
When your viewModel is not accessible globally, there are a couple of options.
First, you can pass any relevant methods using the templateOptions parameter to the template binding.
It would look like (also note that a static template name should be in quotes):
data-bind="template: {name: 'testTemplate', foreach: items, templateOptions: { vmMethod: methodFromMainViewModel } }"
Then, inside of the template vmMethod would be available as $item.vmMethod. If you are using templateOptions as the last parameter, then make sure that there is a space between your braces { { or jQuery templates tries to parse it as its own.
So, you can bind to it like:
<img src="icon.png" data-bind="click: $item.vmMethod" />
The other option is to put a method and a reference to anything relevant from the view model on each item. It looks like you were exploring that option.
Finally, in KO 1.3 (hopefully out in September and in beta soon) there will be a nice way to use something like jQuery's live/delegate functionality and connect it with your viewModel (like in this sample: http://jsfiddle.net/rniemeyer/5wAYY/)
Also, the "Avoiding anonymous functions in event bindings" section of this post might be helpful to you as well. If you are looking for a sample of editing in place using a dynamically chosen template, then this post might help.
This is for those asking how to pass variable methods (functions) to Knockout Template. One of the core features of Templating is the consuming of variable data, which can be String or function. In KO these variables can be embedded in data or foreach properties for the Template to render. Objects embedded in data or foreach, be it String, function etc, can be accessed at this context using $data.
You can look at this code and see if it can help you to pass functions to Knockout Template.
function ViewModel() {
this.employees = [
{ fullName: 'Franklin Obi', url: 'employee_Franklin_Obi', action: methodOne },
{ fullName: 'John Amadi', url: 'employee_John_Amadi', action: methodTwo }
],
this.methodOne = function(){ alert('I can see you'); },
this.methodTwo = function(){ alert('I can touch you'); }
}
ko.applyBindings(new ViewModel());
<ul data-bind="template: { name: employeeTemplate, foreach: employees }" ></ul>
<script type="text/html" id="employeeTemplate">
<li><a data-bind="attr: { href: '#/'+url }, text: fullName, click: $data.action"></a></li>
</script>
If you want to serve multiple Template constructs you can introduce a switch method to your ViewModel like this, and use as property to introduce alias for each item (employee). Make sure you add the switch key, linkable, to the item object.
...
this.employees = [
{ fullName: 'Franklin Obi', linkable : false },
{ fullName: 'John Amadi', url: 'employee_John_Amadi', action: methodTwo, linkable : true }
],
this.methodLinkTemplate = function(employee){return employee.linkable ? "link" : "noLink"; } //this is a two way switch, many way switch is applicable.
...
Then the id of the Template forms will be named thus;
<ul data-bind="template: { name: employeeTemplate, foreach: employees, as: 'employee' }" ></ul>
<script type="text/html" id="noLink">
<li data-bind="text: fullName"></li>
</script>
<script type="text/html" id="link">
<li><a data-bind="attr: { href: '#/'+url }, text: fullName, click: $data.action"></a></li>
</script>
I have not ran this codes but I believe the idea can save someones time.

Categories