MVC: Set value in autocomplete on Edit - model-view-controller

In our MVC application we use jQuery autocomplete control on several pages. This works fine on Create, but I can't make it work on Edit.
Effectively, I don't know how to make the autocomplete controls preload the data from model and still behave as an autocomplete in case the user wants to change the value.
Also how can I make sure that the value is displayed in the same format that is used in Create calls?
All our autocomplete controls have corresponding controllers and all parse Json results.

Let's Try this! Alright Do this:
Suppose you had a list of countries you needed to filter
Auto Complete knows how to some default things by default but suppose you really wanted CountryName and also you know every keypress does an ajax call to the URL you specify.
Create an action method like so:
public ActionResult LookupCountry(string q, int limit)
{
var list = GetListOfCountries(q, 0, limit);
var data = from s in list select new {s.CountryName};
return Json(data);
}
Here is the Jquery:
$(document).ready( function() {
$('#txtCountryName').autocomplete('<%=Url.Action("LookupCountry", "MyController") %>', {
dataType: 'json',
parse: function(data) {
var rows = new Array();
for(var i=0; i<data.length; i++){
rows[i] = { data:data[i], value:data[i].CountryName, result:data[i].CountryName};
}
return rows;
},
formatItem: function(row, i, n) {
return row.CountryName;
},
width: 300,
mustMatch: true,
});
});
Here is the Html
<html><head></head><body>#Html.TextBox("txtCountryName",Model.CountryName)</body></html>
Basically, The magic is in the call to LookUpCountry
The GetCountriesList(string query, int startindex, int limit)
Returns MyCountries.Where(c => c.CountryName.SubString(startindex, limit).Contains(query)).ToList();
So you are making your own trimming function because JQuery has no idea what CountryName is or how to trim it. How ever if it was a javascript object I am not quite sure but do
var jsonString = #Html.GetListOfCountries() //Or Model.Countries.ToJSONString()
var json = JSON.stringify(jsonString); //also JSON.Parse(jsonString) if stringify won't work
which would return the necessary countries as a Html Helper Extension method. And perhaps as a list of javascript objects it would be smart enough to handle it that way in it's native language. However the first approach works for me.

Related

Ajax / jQuery - append new items but check don't already exist in DOM

I have a container div which includes lots of element divs all of which have a unique ID. I then make an ajax call to get more elements and append these to the DOM.
This works using the function below but I need to check that what I append doesn't already exist in the DOM. I've been looking into using each() and possibly remove() or detach() in order to do this, but I am not sure of jQuery syntax and really need some assistance.
function loadMoreItems(url) {
$.get(url, null, function(data) {
var container = $(data).find('#container');
var newItemsHTML = "";
/*-- not sure what to do in between
container.find('.element').remove();
container.each('.element').detach();
--*/
newItemsHTML = $(container).html();
var $newItems = $(newItemsHTML);
$container.isotope('insert', $newItems, true);
}, 'html');
}
<div class="element" id="id_172977"></div>
Assuming all of appended divs has class element you can do
$(".element").each(function() {
container.find("#" + this.id).remove();
});
Demo

There must be an easier way

I am trying to create an JQM app and are doing so by getting a lot of data from database. When I click on a link from a ajax/json generated calendar list I should then be able to get the info for that event by calling the server and get the data. As it is now I do this in 2 steps like this.
My ajax generated event list:
$.each(groupcalendar, function (i, val) {
output += '<li><h2>' + val.matchformname + '</h2><p><strong>' + val.matchday + '</strong></p><p>' + val.coursename + '</p><p class="ui-li-aside"><strong>' + val.matchtime + '</strong></p></li>';
});
When I click on one of the links I want to goto a page called prematchdata.html and get the data fro that specific event. I do so by first calling the click and get the eventid from data-id like this:
$(document).on('click', '#gotoMatch', function () {
var matchid = $(this).attr("data-id");
$.get("http://mypage.com/json/getmatchinfo.php?matchid="+matchid, function(data) {
localStorage["matchinfo"] = JSON.stringify(data);
$.mobile.changePage( "prematchdata.html", { transition: "slide", changeHash: true} );
}, "json");
});
I save the returned data as localStorage and then uses this data in my pageinit like this:
$(document).on("pageinit", "#prematchdata", function() {
var matchinfo = {};
matchinfo = JSON.parse(localStorage["matchinfo"])
var content = '<h2>'+matchinfo["matchname"]+'</h2>';
$('.infoholder').html(content);
});
It works, although for me it seems like the last 2 steps should be done in one, but i am not sure how to do so? It seems a little bit wrong get data, save locally and then use it? Can't this be done without the $(document).on('click', '#gotoMatch', function () {});?
Hoping for some help and thanks in advance :-)
You could try sending it up using a query string. When you're using changePage, change your code like this :
$(document).on('click', '#gotoMatch', function () {
var matchid = $(this).attr("data-id");
$.get("http://mypage.com/json/getmatchinfo.php?matchid=" + matchid, function (data) {
paramData = data[0];
$.mobile.changePage("prematchdata.html", {
transition: "slide",
changeHash: true,
data: paramData //added this extra parameter which will pass data as a query string
});
}, "json");
});
When you're getting it back,
$(document).on("pageinit", "#prematchdata", function() {
var url = $.url(document.location);
var name= url.param("matchname");
var content = '<h2>'+ name +'</h2>';
$('.infoholder').html(content);
});
Another easy way would be use a singlepage template instead of a multi page template. Then, you could just use a global variable to get and set data.
That said, what you're doing right now is more secure than this query string method. By using this, anyone can see what you are sending over the URL. So I advise you keep using localStorage. For more info on this, look into this question.

Is Backbone.js suitable for getting HTML from server?

As far as I can tell, Backbone.js view represents DOM element. I take it from existing DOM or create it on the fly in el attribute.
In my case, I want to take it from server with AJAX request because I'm using Django templates and don't want to rewrite everything to JavaScript templates.
So I define el function that performs AJAX request.
el: function() {
model.fetch().success(function(response) {
return response.template
})
}
Of course, it does NOT work because AJAX request is executed asynchronous.
This means that I don't have el attribute and events does NOT work neither. Can I fix it?
Maybe the Backbone.js framework isn't the right tool for my needs? The reason I want to use that was to have some structure for the code.
P.S. I'm new to Backbone.js.
Do your ajax request from another view, or directly after the page load using jquery directly, and after you've downloaded your template, THEN instantiate your backbone view class with the proper id/el or whatever (depending on where you stored your ajax fetched template). Depending on your use-case, this may or may not be a sensible approach.
Another, perhaps more typical approach, would be to set up your view with some placeholder element (saying "loading" or whatever), then fire off the ajax, and after the updated template has been retrieved, then update your view accordingly (replace the placeholder with the actual template you requested).
When/if you update your view with new/other DOM elements, you need to call the view's delegateEvents method to rebind your events to the new elements, see:
http://backbonejs.org/#View-delegateEvents
I came across a similar requirement. In my instance, I was running asp.net and wanted to pull my templates from user controls. The first thing I would recommend is looking into Marionette because it will save you from writing a lot of boiler plate code in Backbone. The next step is to override how your templates are loaded. In this case I created a function that uses Ajax to retrieve the HTML from the server. I found an example of this function where they were using it to pull down html pages so I did a little modification so I can make MVC type requests. I can't remember where I found the idea from; otherwise, I would give the link here.
function JackTemplateLoader(params) {
if (typeof params === 'undefined') params = {};
var TEMPLATE_DIR = params.dir || '';
var file_cache = {};
function get_filename(name) {
if (name.indexOf('-') > -1) name = name.substring(0, name.indexOf('-'));
return TEMPLATE_DIR + name;
}
this.get_template = function (name) {
var template;
var file = get_filename(name);
var file_content;
var result;
if (!(file_content = file_cache[name])) {
$.ajax({
url: file,
async: false,
success: function (data) {
file_content = data; // wrap top-level templates for selection
file_cache[name] = file_content;
}
});
}
//return file_content.find('#' + name).html();
return file_content;
}
this.clear_cache = function () {
template_cache = {};
};
}
The third step would be to override Marionette's method to load templates. I did this in the app.addInitializer method. Here I am initializing my template loader and setting it's directory to a route handler. So when I want to load a template, I just set the template: "templatename" in my view and Backbone will load the template from api/ApplicationScreens/templatename. I am also overriding my template compiling to use Handlebars because ASP.net is not impressed with the <%= %> syntax.
app.JackTemplateLoader = new JackTemplateLoader({ dir: "/api/ApplicationScreens/", ext: '' });
Backbone.Marionette.TemplateCache.prototype.loadTemplate = function (name) {
if (name == undefined) {
return "";
} else {
var template = app.JackTemplateLoader.get_template(name);
return template;
}
};
// compiling
Backbone.Marionette.TemplateCache.prototype.compileTemplate = function (rawTemplate) {
var compiled = Handlebars.compile(rawTemplate);
return compiled;
};
// rendering
Backbone.Marionette.Renderer.render = function (template, data) {
var template = Marionette.TemplateCache.get(template);
return template(data);
}
Hopefully this helps. I've been working on a large dynamic website and it is coming along very nicely. I am constantly being surprised by the overall functionality and flow of using Marionette and Backbone.js.

JQuery - Iterating JSON Response

The Story So far....
Trying to learn JS and JQuery and i thought i would start with the basics and try alittle AJAX "search as you type" magic. Firstly i just wanted to get the AJAX part right and iterating through the return JSON object and appending it to a unordered list. Im doing no validation on the inputted value vs. the returned JSON results at this time, i just want a controlled way of when to do the AJAX getJSON call. Later i will do the validation once i get this right.
Anyways im having trouble displaying the Account Numbers in in the ul. At the moment the only thing that is being displayed is AccountNumber in the li and not my ACCOUNT NUMBERS
My JS Code is here:
http://jsfiddle.net/garfbradaz/HBYvq/54/
but for ease its here as well:
$(document).ready(function() {
$("#livesearchinput").keydown(function(key) {
$.ajaxSetup({
cache: false
});
$.getJSON(" /gh/get/response.json//garfbradaz/MvcLiveSearch/tree/master/JSFiddleAjaxReponses/", function(JSONData) {
$('<ul>').attr({
id: "live-list"
}).appendTo('div#livesearchesults');
$.each(JSONData, function(i, item) {
var li = $('<li>').append(i).appendTo('ul#live-list');
//debugger;
});
});
});
});​
My JSON file is hosted on github, but again for ease, here it is:
https://github.com/garfbradaz/MvcLiveSearch/blob/master/JSFiddleAjaxReponses/demo.response.json
{
"AccountNumber": [
1000014,
1015454,
1000013,
1000012,
12
]
}
Also here is my Fiddler results proving my JSON object is being returned.
EDIT:
There were so queries about what i was trying to achieve, so here it goes:
Learn JQuery
To build a "Search as you Type" input box. Firstly i wanted to get the AJAX part right first, then i was going to build an MVC3 (ASP.NET) Application that utilises this functionality, plus tidy up the JQuery code which includes validation for the input vs. the returned JSON.
Cheesos answer below worked for me and the JSFiddle can be found here:
http://jsfiddle.net/garfbradaz/JYdTU/
First, I think keydown is probably the wrong time to do the json call, or at least... it's wrong to do a json call with every keydown. That's too many calls. If I type "hello" in the input box, within about .8 seconds, then there are 5 json requests and responses.
But you could make it so that it retrieves the json only the first time through, using a flag.
Something like this:
$(document).ready(function() {
var $input = $("#livesearchinput"), filled = false;
$.ajaxSetup({ cache: false });
$input.keydown(function(key) {
if (!filled) {
filled = true;
$.getJSON("json101.js", function(JSONData) {
var $ul =
$('<ul>')
.attr({id: "live-list"})
.appendTo('div#livesearchesults');
$.each(JSONData, function(i, item) {
$.each(item, function(j, val) {
$('<li>').append(val).appendTo($ul);
});
});
});
}
});
});
The key thing there is I've used an inner $.each().
The outer $.each() is probably unnecessary. The JSON you receive has exactly one element in the object - "AccountNumber", which is an array. So you don't need to iterate over all the items in the object.
That might look like this:
$.each(JSONData.AccountNumber, function(i, item) {
$('<li>').append(item).appendTo($ul);
});
What you probably want is this:
$.each(JSONData.AccountNumber, function(i, item) {
var li = $('<li>').append(item).appendTo('ul#live-list');
});
Your code:
$.each(JSONData, function(i, item) {
var li = $('<li>').append(i).appendTo('ul#live-list');
});
Says "iterate over the keys and values of the outer JSON structure, and print the keys". The first key is "AccountNumber", so you end up printing that.
What you want to do is iterate over the array stored at JSONData.AccountNumber, and print the values:
$.each(JSONData.AccountNumber, function() {
var li = $('<li>').append(this).appendTo('ul#live-list');
});

HTML data from multiple ajax requests to javascript array

I'm trying to pre-load some html content using AJAX and jQuery. The AJAX callback function adds the data to an associative array. I'm fine if I do each request individually:
var contentArray = new Object();
var urlA = "includes/contentA.php";
var urlB = "includes/contentB.php";
var urlC = "includes/contentC.php";
$.get(urlA, function(htmlA) {
contentArray["A"] = htmlA;
});
$.get(urlB, function(htmlB) {
contentArray["B"] = htmlB;
});
$.get(urlC, function(htmlC) {
contentArray["C"] = htmlC;
});
Since I am likely to have a few of these (more than three), I tried to do it a for loop:
var contentArray = new Object();
var pages = new Object();
pages["A"] = "includes/contentA.php";
pages["B"] = "includes/contentB.php";
pages["C"] = "includes/contentC.php";
for (var key in pages) {
var URL = pages[key];
$.get(URL, function(html) {
contentArray[key] = html;
});
}
However, this doesn't work. contentArray only has one property containing html data, rather than three. I'm knew to jQuery, particularly the AJAX stuff, so both explanations and solutions (similar or different-method-same-result) are welome.
By the way, I'm aware that one larger AJAX request is preferable to multiple small ones, but I'm trying to retain compatibility for users without JS enabled, and the current php includes are convenient. Any suggestions as how I might satisfy both these requirements are also very welcome.
Thanks.
The callback function for an AJAX request doesn't run until the request returns. In your case each callback function will use key as it exists in the current context, and since there's no key variable in it's local scope it will use the nearest it can find, the key in your for loop.
The problem is by the time the AJAX requests return, the for loop has been fully iterated over and key is equal to the last key in the array. Thus each of the callback functions will receive the same key, overwriting the previous value in your contentArray.
If you're using jQuery 1.5.1 or above a quick and dirty solution (one that doesn't involve changing the current structure of your PHP files) might be to try the following:
for (var key in pages) {
var URL = pages[key];
$.ajax({
url: URL,
xhrFields: {
'customData': key
},
success: function(html, statusText, jqXHR) {
contentArray[jqXHR.customData] = html;
}
});
}
I haven't tested that but according to the documentation page it should work. All you're doing is using the request object created by jQuery to pass your variable along to the callback function.
Hope that helps

Resources