I want to create a select box for the location field, in which if one types any letter should call API and fetch location details in the dropdown
I tried the below code but didn't work
<select class="js-data-example-ajax form-control" id="FilterLocation"></select>
#Html.Hidden("FilterLocation", new { id = "locationId" })
In the script written below code
function setLocation() {
$('.js-data-example-ajax').select2({
ajax: {
type: 'PUT',
url: function (params) {
return '/api/GoogleCustomSearch/getLocation?matchingName=' + params.term
},
delay: 250,
data: function (params) {
var query = {
}
// Query paramters will be ?search=[term]&page=[page]
return query;
},
processResults: function (data) {
data = JSON.parse(data);
let results = []
if (data.location !== null) {
data.location.forEach((e) => {
results.push({
id: e,
text: e
})
})
}
return {
results: results
};
}
},
placeholder: "Search"
})
$('.js-data-example-ajax').on('change',function(e){
var selVal = $('#FilterLocation').val()
$('#locationId').val(selVal)
//getZipCodeForDynamic(selVal)
})
var $newOption = $("<option selected='selected'></option>")
$("#FilterLocation").append($newOption).trigger('change');
}
Dropdown options are not getting with above code.
Here is a working demo for select2:
View:
<select id="mySelect2" class="js-data-example-ajax" style="width:200px"></select>
js:
$('#mySelect2').select2({
ajax: {
type: 'PUT',
url: "GetSelect2Data",
delay: 250,
data: function(params) {
var query = {
search: params.term,
}
// Query parameters will be ?search=[term]
return query;
},
processResults: function(data) {
//data = JSON.parse(data);
let results = []
if (data.location !== null) {
data.location.forEach((e) => {
results.push({
id: e,
text: e
})
})
}
return {
results: results
};
}
},
placeholder: "Search"
});
model:
public class Select2Model {
public List<string> Location { get; set; }
}
action:
[HttpPut]
public ActionResult GetSelect2Data(string Search)
{
return Json(new Select2Model() { Location = new List<string> { "a"+Search,"b" + Search, "c" + Search } });
}
result:
Related
Hi I would like to bind html inputs with validation response model returned from API like that:
{"userName":[{"memberNames":["UserName"],"errorMessage":"Field User Name is required."}],"acceptTerms":[{"memberNames":["AcceptTerms"],"errorMessage":"Accepting terms is requried"}]}
And my component in mithril
var RegisterPage = {
vm: {
userName: m.prop(),
password: m.prop(),
confirmPassword: m.prop(),
acceptTerms: m.prop(false)
},
controller: function (args) {
this.title = 'Register new user account';
this.vm = RegisterPage.vm;
this.register = function (e) {
e.preventDefault();
apiRequest({ method: "POST", url: "http://localhost:12116/auth/register", data: RegisterPage.vm }).then(RegisterPage.vm.registerResult)
}
},
view: function (ctrl, args) {
return m('form.input-group',
[
m('.input-row', [m('label', 'Email'), m('input[type=email][placeholder=Your email address like myemail#email.com]', { onchange: m.withAttr("value", ctrl.vm.email) })]),
m('.input-row', [m('label', 'Password'), m('input[type=password][placeholder=your password]', { onchange: m.withAttr("value", ctrl.vm.password) })]),
m('.input-row', [m('label', 'Confirm password'), m('input[type=password][placeholder=your password]', { onchange: m.withAttr("value", ctrl.vm.confirmPassword) })]),
m('.input-row', [m('label', 'Accept terms and conditions'), m('input[type=checkbox]', { onchange: m.withAttr("checked", ctrl.vm.acceptTerms) })]),
m('button[type=submit].btn btn-positive btn-block', { onclick: ctrl.register }, 'Register account')
]);
}
}
I am looking for some generic solution. I would like to mark invalid fields with css class and add field validation message.
UPDATE
In my project I use some wrapper around m.request to get more details when 400 is thrown
function apiRequest(args) {
NProgress.start();
if (!args.unwrapError) {
args.unwrapError = function (data, xhr) {
if (xhr.status === 401)
{
layout.badRequestMsg(xhr.statusText);
}
NProgress.done();
return data;
}
}
if (!args.unwrapSuccess) {
args.unwrapSuccess = function (data, xhr) {
NProgress.done();
return data;
}
}
return m.request(args);
}
I have defined a class and am asking m.request to cast a web service's JSON response to it, but each of the class properties come out equal to n/b(), and my view renders each property as function (){return arguments.length&&(a=arguments[0]),a}.
If I do not attempt to auto-cast the JSON response to my class in m.request, then my view renders just fine, which I think tells me that the JSON object returned by the web service is valid JSON.
I want to use my class. What is wrong?
Here is an edited sample of the JSON returned by the web service:
{
"responseHeader":{
"status":0,
"QTime":0,
"params":{
"q":"blah blah",
"indent":"true",
"wt":"json"}
},
"response":{
"numFound":97,
"start":0,
"docs":[
{
"identifier":"abc123",
"heading":"A Great Heading",
"id":"abc-123-1",
"title":"A Title",
"url":"path/to/some.html",
"content":["Blah blah blah blah blee blah."]
},
{
"identifier":"xyz789",
"heading":"Another Heading",
"id":"xyz-789-1",
"title":"Another Title",
"url":"another/path/to.html",
"content":["My bonny lies over the ocean."]
}
]
}
}
Here is my Mithril app:
var findus = {};
findus.Document = function (data) {
this.id = m.prop(data.id);
this.title = m.prop(data.title);
this.heading = m.prop(data.heading);
this.identifier = m.prop(data.identifer);
this.url = m.prop("//" + data.url + "#" + data.identifier);
};
findus.vm = (function() {
var vm = {};
vm.init = function () {
// user input
vm.queryText = m.prop("");
vm.search = function () {
if (vm.queryText()) {
vm.results = m.request({
method: "GET",
url: "/prd/query?q=" + vm.queryText(),
type: findus.Document,
unwrapSuccess: function (response) {
return response.response.docs;
},
unwrapError: function (response) {
console.log(response);
}
}).bind(vm);
}
};
};
return vm;
}());
findus.controller = function () {
findus.vm.init();
};
findus.view = function () {
return [
m("input", {onchange: m.withAttr("value", findus.vm.queryText), value: findus.vm.queryText()}),
m("button", {onclick: findus.vm.search}, "Search"),
findus.vm.results ? m("div", [
findus.vm.results().map(function (result) {
return m("div", [
m("h2", result.heading),
m("p", result.content),
m("a", {href: result.url}, result.url)
]);
})
]) : ""
];
};
m.module(document.body, {controller: findus.controller, view: findus.view});
Oh, bugger. I forgot that my class properties are getter/setters via m.prop, so I should have been calling them as functions in the view -- see below.
False alarm, problem solved, I'm embarrassed.
findus.view = function () {
return [
m("input", {onchange: m.withAttr("value", findus.vm.queryText), value: findus.vm.queryText()}),
m("button", {onclick: findus.vm.search}, "Search"),
findus.vm.results ? m("div", [
findus.vm.results().map(function (result) {
return m("div", [
m("h2", result.heading()),
m("p", m.trust(result.content())),
m("a", {href: result.url()}, result.url())
]);
})
]) : ""
];
};
i want to perform filtering,sorting and no of records in kendo grid but it is not working.
this is my view page:
<script>
$(document).ready(function () {
$("#categories-grid").kendoGrid({
dataSource: {
type: "json",
transport: {
read: {
url: "#Html.Raw(Url.Action("categoriesList", "Admin"))",
type: "POST",
dataType: "json",
data: '',
}
},
schema: {
data: "Data",
total: "Total",
errors: "Errors"
},
error: function(e) {
display_kendoui_grid_error(e);
// Cancel the changes
this.cancelChanges();
},
pageSize: 2,
serverPaging: true,
serverFiltering: true,
serverSorting: true
},
pageable: {
refresh: true,
pageSizes: [10,20,30]
},
editable: {
confirmation: false,
mode: "inline"
},
scrollable: false,
columns: [{
field: "CategoryName",
title: "CategoryName",
width: 100
}, {
field: "CategoryId",
title: "Edit",
width: 100,
template: 'Edit'
}]
});
});
</script>
This is my controller side http post action:
[HttpPost]
public ActionResult categoriesList(DataSourceRequest command)
{
Categories categoriesBal = new Categories();
List<CategoryModel> categoriesList = new List<CategoryModel>();
var category = GetCategory();
ViewBag.Category = GetCategory();
List<Category> categoryDetails = categoriesBal.fetchCategory();//here i am fetching categoryid,name
var gridModel = new DataSourceResult
{
Data = categoryDetails.Select(x =>
{
var categoryModel = new CategoryModel();
categoryModel.CategoryId = x.CategoryId;
categoryModel.CategoryName = x.Name;
return categoryModel;
}),
Total = categoryDetails.Count
};
return Json(gridModel);
}
This is my DataSourceRequest class
public class DataSourceRequest
{
public int Page { get; set; }
public int PageSize { get; set; }
public DataSourceRequest()
{
this.Page = 1;
this.PageSize = 10;
}
}
This is my Category model:
public class CategoryModel
{
public int CategoryId { get; set; }
public string CategoryName { get; set; }
public int frequency { get; set; }
public virtual ICollection<SubcategoryModel> subCategory { get; set; }
}
i am having 12 category.when i am setting item per page dat is 2 it display all records.
can any body tell me what is the problem in my code and how to perform sorting and filtering??
Change your schema to act as a function
schema: {
data: function(response) {
return response.Data ;
}
total: function(response) {
return response.Total;
}
},
and pageable to
pageable: {
refresh: true,
pageSizes: [2,10,20,30]
},
and do not see the use of DataSourceRequest in your code since you don't send the pageSize from the read . send a object of DataSourceRequest and return only what is specify in your DataSourceRequest . in this case only two records
transport: {
read: function (options) {
var commandOBJ=[{
Page: 1, // Once the first two item is loaded and you click for the next page you will have the page in "options" (should be like options.page)
PageSize:2
}];
$.ajax({
url:"#Html.Raw(Url.Action("categoriesList", "Admin"))",
data: { command: bookmarkID },
dataType: "json",
cache: false,
success: function (result) {
options.success(result);
},
error: function (result) {
options.error(result);
}
});
}
}
Now from your action categoriesList check the command and send data accordingly . and your read action can be a GET instead of POST . hope this Helps
Instead of using your custom DataSourceRequest class, use the KendoGridMvcRequest class from this project, this class correctly maps the paging, sorting and filtering.
Also available as nuget.
See this link for demo.
function getListPhoneNumbers() {
var data = {listContacts:[{name:'Ho Cong Vi',number:'12345666'},{name:'hcv',number:'6543218'}]};
WL.Logger.info('Data:'+JSON.stringify(data));
return data;
}
function addListPhoneNumber(data) {
WL.Logger.debug('Add Data to JSONStore: ' + data);
return;
}
function updateListPhoneNumber(data) {
WL.Logger.debug('Updata Data from JSONStore: ' + data);
return;
}
function deleteListPhoneNumber(data) {
WL.Logger.debug('Delete Data from JSONStore: ' + data);
return;
}
This is my code in main.js:
$('#show-all-btn').on('click', showAllData);
var collectionName = 'Contacts',
collections = {};
collections[collectionName] = {
searchFields: {
name: 'string',
number: 'string'
},
adapter: {
name: 'listPhoneNumbers',
add: 'addListPhoneNumber',
replace: 'updateListPhoneNumber',
remove: 'deleteListPhoneNumber',
load: {
procedure: 'getListPhoneNumbers',
param: [],
key: 'listContacts'
}
}
};
WL.JSONStore.init(collections)
function showAllData() {
$('#show-all-btn').on("click", function() {
$('#info').show();
});
WL.JSONStore.get(collectionName).load().then(function(res) {
alert('ok' + JSON.stringify(res));
}).fail(function(errorObject) {
alert(errorObject);
});
}
This is the error:
[wl.jsonstore] {"src":"load","err":18,"msg":"FAILED_TO_LOAD_INITIAL_DATA_FROM_ADAPTER_INVALID_LOAD_OBJ","col":"Contact","usr":"jsonstore","doc":{},"res":{}
The error message is saying the load object you passed is invalid. This is probably because you passed param instead of params. Notice the s at the end.
Also, this code:
WL.JSONStore.init(collections)
function showAllData() {
$('#show-all-btn').on("click", function() {
$('#info').show();
});
WL.JSONStore.get(collectionName).load().then(function(res) {
alert('ok' + JSON.stringify(res));
}).fail(function(errorObject) {
alert(errorObject);
});
}
Looks wrong, maybe what you meant to write is something like this:
WL.JSONStore.init(collections).then(function () {
WL.JSONStore.get(collectionName).count().then(function (numberOfDocsInCollection) {
if(numberOfDocsInCollection < 1) {
WL.JSONStore.get(collectionName).load().then(function(res) {
//handle success
})
}
})
});
I omitted handling failures for brevity. Note that the load will will duplicate items in the collection if those items already exist, hence the count to check if the collection is empty or not.
I have four controls on the page, a simple form with first and last names, date of birth and this drop down that contains some names of countries. When I make changes to the these controls I am able to see those changes in my viewModel that is passed in as a parameter in the SavePersonDetails POST below, but I never see the LocationId updated in that view model and I am not sure why.
This is what I have in my markup, Index.cshtml:
#model Mvc4withKnockoutJsWalkThrough.ViewModel.PersonViewModel
#using System.Globalization
#using Mvc4withKnockoutJsWalkThrough.Helper
#section styles{
#Styles.Render("~/Content/themes/base/css")
<link href="~/Content/Person.css" rel="stylesheet" />
}
#section scripts{
#Scripts.Render("~/bundles/jqueryui")
<script src="~/Scripts/knockout-2.1.0.js"></script>
<script src="~/Scripts/knockout.mapping-latest.js"></script>
<script src="~/Scripts/Application/Person.js"></script>
<script type="text/javascript">
Person.SaveUrl = '#Url.Action("SavePersonDetails", "Person")';
Person.ViewModel = ko.mapping.fromJS(#Html.Raw(Json.Encode(Model)));
var userObject = '#Html.Raw(Json.Encode(Model))';
var locationsArray = '#Html.Raw(Json.Encode(Model.Locations))';
var vm = {
user : ko.observable(userObject),
availableLocations: ko.observableArray(locationsArray)
};
ko.applyBindings(vm);
</script>
}
<form>
<p data-bind="with: user">
Your country:
<select data-bind="options: $root.availableLocations, optionsText: 'Text', optionsValue: 'Value', value: LocationID, optionsCaption: 'Choose...'">
</select>
</p>
</form>
This is my View Model:
public class PersonViewModel
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime DateOfBirth { get; set; }
public string LocationId { get; set; }
public IEnumerable<SelectListItem> Locations { get; set; }
}
I have a simple controller that loads my Person and a drop down list containing three countries.
private PersonViewModel _viewModel;
public ActionResult Index()
{
var locations = new[]
{
new SelectListItem { Value = "US", Text = "United States" },
new SelectListItem { Value = "CA", Text = "Canada" },
new SelectListItem { Value = "MX", Text = "Mexico" },
};
_viewModel = new PersonViewModel
{
Id = 1,
FirstName = "Test",
LastName = "Person",
DateOfBirth = new DateTime(2000, 11, 12),
LocationId = "", // I want this value to get SET when the user changes their selection in the page
Locations = locations
};
_viewModel.Locations = locations;
return View(_viewModel);
}
[HttpPost]
public JsonResult SavePersonDetails(PersonViewModel viewModel)
{
int id = -1;
SqlConnection myConnection = new SqlConnection("server=myMachine;Trusted_Connection=yes;database=test;connection timeout=30");
try
{
// omitted
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
finally
{
myConnection.Close();
}
return Json(id, "json");
}
Lastly, here is my Person.js file, I am using knockout
var Person = {
PrepareKo: function () {
ko.bindingHandlers.date = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
element.onchange = function () {
var observable = valueAccessor();
observable(new Date(element.value));
}
},
update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
var observable = valueAccessor();
var valueUnwrapped = ko.utils.unwrapObservable(observable);
if ((typeof valueUnwrapped == 'string' || valueUnwrapped instanceof String) && valueUnwrapped.indexOf('/Date') === 0) {
var parsedDate = Person.ParseJsonDate(valueUnwrapped);
element.value = parsedDate.getMonth() + 1 + "/" + parsedDate.getDate() + "/" + parsedDate.getFullYear();
observable(parsedDate);
}
}
};
},
ParseJsonDate: function (jsonDate) {
return new Date(parseInt(jsonDate.substr(6)));
},
BindUIwithViewModel: function (viewModel) {
ko.applyBindings(viewModel);
},
EvaluateJqueryUI: function () {
$('.dateInput').datepicker();
},
RegisterUIEventHandlers: function () {
$('#Save').click(function (e) {
// Check whether the form is valid. Note: Remove this check, if you are not using HTML5
if (document.forms[0].checkValidity()) {
e.preventDefault();
$.ajax({
type: "POST",
url: Person.SaveUrl,
data: ko.toJSON(Person.ViewModel),
contentType: 'application/json',
async: true,
beforeSend: function () {
// Display loading image
},
success: function (result) {
// Handle the response here.
if (result > 0) {
alert("Saved");
} else {
alert("There was an issue");
}
},
complete: function () {
// Hide loading image.
},
error: function (jqXHR, textStatus, errorThrown) {
// Handle error.
}
});
}
});
},
};
$(document).ready(function () {
Person.PrepareKo();
Person.BindUIwithViewModel(Person.ViewModel);
Person.EvaluateJqueryUI();
Person.RegisterUIEventHandlers();
});
As you can see, I have the data in the page but none of them show as selected
Your solution is overly complex and is leading to certain weirdness with your data. Instead of trying to patch the Titanic, your best bet is to start over and simplify:
Your page's model contains all the information you need. There's no need to try to create two totally separate view models to work with the user data versus locations. With the mapping plugin, you can specify different "view models" for various objects in your main view model, and there's a simpler pattern that can be followed to set all that up. Here's what I would do:
// The following goes in external JS file
var PersonEditor = function () {
var _init = function (person) {
var viewModel = PersonEditor.PersonViewModel(person);
ko.applyBindings(viewModel);
_wireEvents(viewModel);
}
var _wireEvents = function (viewModel) {
// event handlers here
}
return {
Init: _init
}
}();
PersonEditor.PersonViewModel = function (person) {
var mapping = {
'Locations': {
create: function (options) {
return new PersonEditor.LocationViewModel(options.data)
}
}
}
var model = ko.mapping.fromJS(person, mapping);
// additional person logic and observables
return model;
}
PersonEditor.LocationViewModel = function (location) {
var model = ko.mapping.fromJS(location);
// additional location logic and observables
return model;
}
// the following is all you put on the page
<script src="/path/to/person-editor.js"></script>
<script>
$(document).ready(function () {
var person = #Html.Raw(#Json.Encode(Model))
PersonEditor.Init(person);
});
</script>
Then all you need to bind the select list to the locations array is:
<p>
Your country:
<select data-bind="options: Locations, optionsText: 'Text', optionsValue: 'Value', value: LocationId, optionsCaption: 'Choose...'">
</select>
</p>
Based on your updated question, do this.
1.We do not need locationsArray actually. Its already in user.Locations (silly me)
2.On Index.cshtml, page change the JavaScript like this.
var userObject = #Html.Raw(Json.Encode(Model)); // no need for the quotes here
jQuery(document).ready(function ($) {
Person.PrepareKo();
Person.EvaluateJqueryUI();
Person.RegisterUIEventHandlers();
Person.SaveUrl = "#Url.Action("SavePersonDetails", "Knockout")";
Person.ViewModel = {
user : ko.observable(userObject)
};
Person.BindUIwithViewModel(Person.ViewModel);
});
3.On your Person.js page, inside RegisterUIEventHandlers #Save button click event, do this.
$('#Save').click(function (e) {
var updatedUser = Person.ViewModel.user();
updatedUser.Locations.length = 0; // not mandatory, but we don't need to send extra payload back to server.
// Check whether the form is valid. Note: Remove this check, if you are not using HTML5
if (document.forms[0].checkValidity()) {
e.preventDefault();
$.ajax({
type: "POST",
url: Person.SaveUrl,
data: ko.toJSON(updatedUser), // Data Transfer Object
contentType: 'application/json',
beforeSend: function () {
// Display loading image
}
}).done(function(result) {
// Handle the response here.
if (result > 0) {
alert("Saved");
} else {
alert("There was an issue");
}
}).fail(function(jqXHR, textStatus, errorThrown) {
// Handle error.
}).always(function() {
// Hide loading image.
});
}
});
5.Finally, our markup
<p data-bind="with: user">
Your country:
<select data-bind="options: Locations,
optionsText: 'Text',
optionsValue: 'Value',
value: LocationId,
optionsCaption: 'Choose...'">
</select>
</p>
On an unrelated side-note,
The jqXHR.success(), jqXHR.error(), and jqXHR.complete() callbacks are
deprecated as of jQuery 1.8. To prepare your code for their eventual
removal, use jqXHR.done(), jqXHR.fail(), and jqXHR.always() instead.