I have following kendo example with a custom edit template:
In the example there is a custom edit template, so when you double click on the calendar to make a new event this will show with the custom fields.
There is a custom field for "Contacts" which has an array as data source.
This data source is an array I get from the server (takes 1-2 seconds to get).
The fact that the edit template is prepared with tags makes it not possible to simply create in my success (or done) handler of the ajax call that gets the data.
The only way I see is to have the data ready at page load so that the template picks it up.
I want however to either create the template whenever my data load is done or add my data to it after it is loaded.
To simulate the time the server takes for the data to load I use setTimeout of 1 sec, that way the edit template does not pick up the data.
To recreate:
double click on the calendar to create an event
notice that the contact field is empty (because data is not ready at page load)
Any help appreciated
This has nothing to do with the async delay. Your kontaktdata array is local to the anonymous function you pass to setTimeout, so it simply doesn't exist in the context the template is evaluated in.
Your data has to either be defined on the data model itself or in the global context.
The other problem is that the data structure itself has to exist - either a kendo.data.DataSource or an array, and you need to update it with new data if you want an existing view to be aware of that new data. If you simply replace it, the edit template has no way of picking that up immediately (if you open a new edit dialog, it will also work, of course).
So if you do this, for example, it will work:
var kontaktdata = [];
setTimeout(function(){
kontaktdata.push.apply(kontaktdata, [
{ text: "Demo B Client", value: 1 },
{ text: "Martin", value: 2 },
{ text: "Herbert", value: 3 }]);
}, 4000);
Related
I have a view that displays both the search screen and the results list. To start the results list is hidden.
On clicking the search button, instead of the view posting back to the controller using a Html.BeginForm, it has a JavaScript event handler which is called.
The Handler passes over a complex object, in the form of:
{ "searchData":
{ "ticketNumberCompare":0,
"ticketSearchTextFrom":"0",
"ticketSearchTextTo":null,
"ticketDateCompare":1,
"startDate":"2017-01-06T00:00:00",
"endDate":"2017-01-13T00:00:00",
...
etc
...
},
"searchMode":
{ "mode":2,
"pageNumber":1,
"pageSize":5,
"sortDirection":"desc",
...
etc
...
"ValidationErrorMessages":null
}
}
By using Json.Stringify, I have got the above structure to be converted for use by a function within the c# MVC 5 controller, defined as follows.
[HttpPost]
public JsonResult GetSearchResultsJson(ComplexObject searchCriteria, int? page)
{
}
This function returns back html which is rendered from a Razor view using a model that contains the results from the database.
When I click on the submit button for the initial search all works and I get a partial view of five records displayed with the correct pageList values displayed.
When I click on the pageList items I get a page not found error.
On checking the ajax call for the initial load it is passing over the Json.Stringify of the structure at the top of this question. But the pageList items are not, they just pass over the page
Addendum:
Oh I forgot to mention that I have upgraded this function to use an object instead of the four string that used to be passed, because a new super search now has around 30 properties. Before the change the code worked fine.
I am using knockout in my project. I have multiple viewmodel, each viewmodel have its own save function implemented in it. Now whenever user clicks on save button the viewmodel data post to the server, i want to block the save button until the response came back from server.
Currently i am handling this by creating an extra observable property saving in each viewmodel. So when user click over the save button i am setting saving observable to true and in callback i am setting it to false. And i have bind this saving property with the button using knockout disable binding.
But i feel that this approach is not good and it contains the following big drawbacks:
For this i have to add an extra property in each viewmodel.
I have to add multiple line of code like setting it to true and again set it to false.
The approach is not centralize, the code for this approach is scattered.
So i want to know is there any other better way to handle this, a plugin or some standard way ??
Edit
Just to clarify, my question has nothing to do with asp.net postback, the question is how i can handle efficiently the ajax, like block the save button, displaying the response message etc
??
This is generally what makes a viewmodel a viewmodel. In a pattern like MVC, your controller shouldn't really have any idea what your view looks like, what controls it has, or what it's state is, and your model only contains data for the view to model. In an MVVM pattern, as knockout is, your viewModel actually does have knowledge of the current states of controls on the view. This isn't to say your viewmodel should directly update the view, but it usually will contain properties that are bound to states of the view. Things like SaveButtonEnabled or IsSavingData or even things like StatusLabelColor are accepted in a viewmodel.
Perhaps use $.ajaxSetup(). You call this in your document ready function.
$.ajaxSetup({
beforeSend: function(jqXHR)
{
//this will be called before every
//ajax call in your program
//so perhaps, increment an observable viewmodel variable
//representing the number of outstanding requests
//if this variable is > 0 then disable
//your button
},
complete: function(jqXHR)
{
//this will be called after every
//call returns
//decrement your variable here
//if variable is zero, then enable button
}
});
I'd recommend you take a look at http://durandaljs.com/, a framework using Knockout and has some great data patterns, even if you don't use it directly.
I'm pretty new to Backbone.js, loving it so far, but I'm having a little trouble trying to get relational data to render.
Within my Backbone view (called ImagesView) I have the following code:
// Render it
render: function () {
var self = this;
// Empty the container first
self.$el.html("")
// Loop through images
self.collection.each(function(img){
// convert `img` to a JSON object
img = img.toJSON()
// Append each one
self.$el.append(self.template(img))
}, self)
}
There are 3 images in the collection, and they are templated correctly with the above code. Within the img object is a user attribute, containing the User ID. I'm trying to return the user's details, and use these within the template instead of the ID. I'm doing that using the code below:
// Render it
render: function () {
var self = this;
// Empty the container first
self.$el.html("")
// Loop through images
self.collection.each(function(img){
// convert `img` to a JSON object
img = img.toJSON()
/* New code START */
// Each img has a `user` attribute containing the userID
// We'll use this to get their details
$.getJSON('/user/' + img.user, {}, function(json, textStatus) {
img.photographer = {
id: json.id,
username: json.username,
real_name: json.real_name
}
/* Moved 1 level deeper */
// Append each one
self.$el.append(self.template(img))
});
/* New code END */
}, self)
}
When I do this, the user's details are returned correctly and inserted into the template, but I now get 3 of each image returned instead of 1 (i.e. 9 in total), in a completely random order. What am I doing wrong? I'm open to using Backbone methods instead of the getJSON if that will make it easier, I just couldn't get it to work myself. I'm using underscore.js for the templating
The random order is likely caused by the requests being fired at very close intervals and responses returning out of the order they were fired in. I'm not sure why you're getting the multiple things, but if your template renders everything and you're calling that 3 times that could be it?
Anyway where I think you're going wrong is putting the responsibility of loading data into the render method. You'd want this to be abstracted so you have a good separation between data concerns and template concerns. As the ordering of the data is of interest, you'll want all 3 requests to have loaded before rendering. There's two different approaches you could take to this depending on if prior to loading this data you have sufficient data to render the view:
If you're waiting on all the data prior to rendering the view then you would want to render a different view (or template of this view) whilst the data is loaded and then replace that with a view showing all the data once it is loaded.
If you have sufficient data to render the view and what you are loading is supplementary, you'd want to render the view with the data you have in render and then once the other data is loaded use a custom method to modify the rendered view to include your data.
If you want to find out when all 3 requests are complete you can use http://api.jquery.com/jquery.when/
I have a list of images each with a 'Like' button. When the 'Like' button is clicked, an AJAX request (containing the item_id and user_id) will be sent to the serverside to record the Like (by adding a new row in the table likes with values for item_id and user_id).
The model Photo is used for the images displayed on the page. If I understand correctly, this.model.save() is used if I want to update/add a new Photo, so it is not suitable for recording 'Likes'. Therefore, I have to use something like $.get() or $.post(). Is this the conventional way?
Or do I create a new model called Like as shown below, which seems to make it messier to have a View and template just for a Like button.
Like = Backbone.Model.extend({
url: 'likes'
});
LikeView = Backbone.View.extend({
template: _.template( $('#tpl-like').html() ),
events: {
'click .btn_like': 'like'
},
like: function() {
this.model.save({
user_id: 1234,
post_id: 10000
})
}
});
In similar cases to this I've used the $.get method rather than create a new model, obviously this will depend on your application, but here are my reasons.
This case appears to have the following characteristics:
Like is a relationship between a person and a photo,
you seem to have a server side resource that accepts the photo and user ids to create this relationship already,
you probably have no other information attached to that relationship, and
you probably don't have significant view logic to go with the like itself
This is better handled by adding another attribute to your Photo object, that contains the number of likes. Then use $.get to create the like, and a 200 response will simply update the photo object to up it's count (and hence the view). Then the server side just needs to include the like count as part of it when it returns.
I'm assuming here that once a like is made you won't be updating it. If you do need to update or delete it, I might still keep using the $.get. You can add a likes array to your photo object where each element is the id of the like resource. The view will display the length of the array as the count, and if you need to delete the like, you have access to the id and you can use $.post. Just make sure you don't use .push to add values to your array since that'll bypass backbone's set method and you won't get your event callbacks. You need to clone the array, then push, and then set it when you make changes.
To learn backbone Im creating a Twitter like app. So you know that Twitter sends a GET request to the server every N seconds to check for new tweets. If there are new tweets, it creates the hidden li elements and shows the button with "N new Tweets". If you click it, it shows the hidden li elements, showing the new tweets.
But the behaviour is different when you add a new tweet: the tweet is visible. You don't have to click the button to see it.
I already have made the first part, for the hidden tweets. For the part of posting a new tweet and showing it direclty, I thought it would be easy to do by creating the new model, calling collection.create() and triggering the right event, something like:
var newTweet = new Tweet();
newTweet.set( /* set the attributes here. Some attributes are missing, because they are calculated server side */ );
var created_tweet = this.collection.create( newTweet, { silent: true, wait: true } ); // I choose silent=true because the add event on my collection is in charge of adding the new hidden tweets when there are new ones on the server
this.collection.trigger("posted_new_tweet", created_tweet);
Then, my collection is subscribed to the event "posted_new_tweet", so every time a user posts a new tweet, a specific method of my collection is being called.
This approach was working fine until I got errors due to the variable created_comment passed in the trigger: it is not "complete". I mean that the model has some attributes like "id" or *"created_on"* that are undefined. These attributes are calculated server side, but I thought that if I passed wait=true, it would wait and update my model with the response given by the server (when a POST request is made to the server, it returns the new created model in json)
Shouldn't my model have the server side attributes aswell? Is it the right approach for such a thing? In case that it is not, how can I have 2 different methods to display a collection view?
Thank you!
create is still asynchronous even if you pass { wait: true }. The difference is without wait the model will get added to the collection immediately while with wait backbone won't add it to the collection until a success response from the server.
What you should do is add a success callback to create that fires the event upon the server response.
var created_tweet = this.collection.create( newTweet, { silent: true, wait: true, success: this.successCallback } );
// Add successCallback as a method to the collection
successCallback: function(collection, response) {
// I'm not 100% positive which values are passed to this function. Collection may actually be the new model.
this.collection.trigger("posted_new_tweet", created_tweet);
}