I'm trying to chain two, possibly three <g:select ...> statements together using Ajax like is shown here Populate dropdown list using ajax In grails but all the examples I find have two big differences from what I'm using. 1. I'm using the jQuery library, not prototype. And 2. I don't have domain objects for my select values, they are pulled from an Oracle table via a service call.
My problem looks like this:
<g:select name="degreeSubject" from="${majors}" noSelection="${['':'-Choose Subject-']}" value="${degreeInstance?.degreeSubject }"/>
<g:select name="degreeConcentration" from="${concentrations}" noSelection="${['':'']}" value="${degreeInstance?.degreeConcentration }"/>
Where the majors, and concentrations come through the controller but are populated in a service class.
I was thinking the controller method would look something like
def updateSelect = {
def concentrations = degreeService.getConcentrations(params.selectedValue)
render (template:"selectConcentration", model : ['concentrations' : concentrations])
}
But, I can't get it to work.
Thoughts? Or someone have an example of doing this with jQuery and no domain objects using Grails 2.2.4?
You can really do it without being javascript-library specific. If you use the grails built-in remoteFunction it will handle the jQuery portion for you. What you would then want for your degreeSubject select is:
<g:select name="degreeSubject"
from="${majors}"
noSelection="${['':'-Choose Subject-']}"
value="${degreeInstance?.degreeSubject }"
onChange="${remoteFunction(
controller: 'yourControllerName',
action: 'updateSelect',
params: '\'value=\' + escape(this.value),
onSuccess: 'updateConcentration(data)')}/>
The key being the onChange event calling the remoteFunction. The remote function will make an ajax call to whatever controller action you want, but you'll need to call a javascript function to take in the results of your controller action and populate the other select. If you wanted to do this with simple js you could do this:
function updateConcentration(items) {
var control = document.getElementById('degreeConcentration')
// Clear all previous options
var i = control.length
while (i > 0) {
i--
control.remove(i)
}
// Rebuild the select
for (i=0; i < items.length; i++) {
var optItem = items[i]
var opt = document.createElement('option');
opt.text = optItem.value
opt.value = optItem.id
try {
control.add(opt, null) // doesn't work in IE
}
catch(ex) {
control.add(opt) // IE only
}
}
}
and finally your controller action should look like this:
def updateSelect(value) = {
def concentrations = degreeService.getConcentrations(value)
render concentrations as JSON // or use respond concentrations if you upgrade to 2.3
}
Related
I have a Kendo.MVC project. The view has a model with a field of type List<>. I want to populate the List from a Javascript function. I've tried several ways, but can't get it working. Can someone explain what I'm doing wrong?
So here is my model:
public class Dashboard
{
public List<Note> ListNotes { get; set; }
}
I use the ListNotes on the view like this:
foreach (Note note in Model.ListNotes)
{
#Html.Raw(note.NoteText)
}
This works if I populate Model.ListNotes in the controller when the view starts...
public ActionResult DashBoard(string xsr, string vst)
{
var notes = rep.GetNotesByCompanyID(user.ResID, 7, 7);
List<Koorsen.Models.Note> listNotes = new List<Koorsen.Models.Note>();
Dashboard employee = new Dashboard
{
ResID = intUser,
Type = intType,
FirstName = user.FirstName,
LastName = user.LastName,
ListNotes = listNotes
};
return View(employee);
}
... but I need to populate ListNotes in a Javascript after a user action.
Here is my javascript to make an ajax call to populate ListNotes:
function getReminders(e)
{
var userID = '#ViewBag.CurrUser';
$.ajax({
url: "/api/WoApi/GetReminders/" + userID,
dataType: "json",
type: "GET",
success: function (notes)
{
// Need to assign notes to Model.ListNotes here
}
});
}
Here's the method it calls with the ajax call. I've confirmed ListNotes does have the values I want; it is not empty.
public List<Koorsen.Models.Note> GetReminders(int id)
{
var notes = rep.GetNotesByCompanyID(id, 7, 7);
List<Koorsen.Models.Note> listNotes = new List<Koorsen.Models.Note>();
foreach (Koorsen.OpenAccess.Note note in notes)
{
Koorsen.Models.Note newNote = new Koorsen.Models.Note()
{
NoteID = note.NoteID,
CompanyID = note.CompanyID,
LocationID = note.LocationID,
NoteText = note.NoteText,
NoteType = note.NoteType,
InternalNote = note.InternalNote,
NoteDate = note.NoteDate,
Active = note.Active,
AddBy = note.AddBy,
AddDate = note.AddDate,
ModBy = note.ModBy,
ModDate = note.ModDate
};
listNotes.Add(newNote);
}
return listNotes;
}
If ListNotes was a string, I would have added a hidden field and populated it in Javascript. But that didn't work for ListNotes. I didn't get an error, but the text on the screen didn't change.
#Html.HiddenFor(x => x.ListNotes)
...
...
$("#ListNotes").val(notes);
I also tried
#Model.ListNotes = notes; // This threw an unterminated template literal error
document.getElementById('ListNotes').value = notes;
I've even tried refreshing the page after assigning the value:
window.location.reload();
and refreshing the panel bar the code is in
var panelBar = $("#IntroPanelBar").data("kendoPanelBar");
panelBar.reload();
Can someone explain how to get this to work?
I don't know if this will cloud the issue, but the reason I need to populate the model in javascript with an ajax call is because Model.ListNotes is being used in a Kendo Panel Bar control and I don't want Model.ListNotes to have a value until the user expands the panel bar.
Here's the code for the panel bar:
#{
#(Html.Kendo().PanelBar().Name("IntroPanelBar")
.Items(items =>
{
items
.Add()
.Text("View Important Notes and Messages")
.Expanded(false)
.Content(
#<text>
#RenderReminders()
</text>
);
}
)
.Events(e => e
.Expand("getReminders")
)
)
}
Here's the helper than renders the contents:
#helper RenderReminders()
{
if (Model.ListNotes.Count <= 0)
{
#Html.Raw("No Current Messages");
}
else
{
foreach (Note note in Model.ListNotes)
{
#Html.Raw(note.NoteText)
<br />
}
}
}
The panel bar and the helpers work fine if I populate Model.ListNotes in the controller and pass Model to the view. I just can't get it to populate in the javascript after the user expands the panel bar.
Perhaps this will do it for you. I will provide a small working example I believe you can easily extend to meet your needs. I would recommend writing the html by hand instead of using the helper methods such as #html.raw since #html.raw is just a tool to generate html in the end anyways. You can write html manually accomplish what the helper methods do anyway and I think it will be easier for you in this situation. If you write the html correctly it should bind to the model correctly (which means it won't be empty on your post request model) So if you modify that html using javascript correctly, it will bind to your model correctly as well.
Take a look at some of these examples to get a better idea of what I am talking about:
http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/
So to answer your question...
You could build a hidden container to hold your list values like this (make sure this container is inside the form):
<div id="ListValues" style="display:none">
</div>
Then put the results your ajax post into a javascript variable (not shown).
Then in javascript do something like this:
$('form').off('submit'); //i do this to prevent duplicate bindings depending on how this page may be rendered futuristically as a safety precaution.
$('form').on('submit', function (e) { //on submit, modify the form data to include the information you want inside of your ListNotes
var data = getAjaxResults(); //data represents your ajax results. You can acquire and format that how you'd like I will use the following as an example format for how you could save the results as JSON data: [{NoteID ="1",CompanyID ="2"}]
let listLength = data.length;
for (let i = 0; i < listLength; i++) {
$('#ListValues').append('<input type="text" name="ListNotes['+i+'].NoteID " value="' + data.NoteID +'" />')
$('#ListValues').append('<input type="text" name="ListNotes['+i+'].CompanyID " value="' + data.CompanyID +'" />')
//for your ajax results, do this for each field on the note object
}
})
That should do it! After you submit your form, it should automatically model bind to you ListNotes! You will be able to inpsect this in your debugger on your post controller action.
I'm quite new here so if I do something wrong let me know, ok?
I'm quite new in web development as well.
I'm having a problem here with a post method in ASP.NET.
Please, don't mind the name of the buttons and methods, ok? I'm Brazilian and their names are all in portuguese.
I have a submit button that calls a ng-click (Angularjs) method called AdicionarCliente().
View
<div>
<input type="submit" class="btn btn-info" value="Salvar" ng-click="AdicionarCliente()"/>
</div>
JavaScript
myApp.controller('AdicionarClientesController', function ($scope, $http) {
$scope.NomeCliente = "";
$scope.Telefone1Cliente = "";
$scope.AdicionarCliente = function () {
var promisse = $http.post("/app/AdicionarCliente/", { NomeCliente: $scope.NomeCliente, Telefone1Cliente: $scope.Telefone1Cliente })
promisse.then(function () {
window.location.href = "CadastroPet";
return false;
});
};
It works well until this part. All the times that I hit the submit button, it comes here and enter the function in the variable "promisse".
Now - the problem is here:
Controller
[HttpPost]
public JsonResult AdicionarCliente(string NomeCliente, string Telefone1Cliente)
{
var db = new RexsoftEntities();
db.CLIENTES.Add(new CLIENTES() { NOME = NomeCliente,
TELEFONE1 = Telefone1Cliente});
db.SaveChanges();
var Clientes = db.CLIENTES.ToList();
return Json(Clientes, JsonRequestBehavior.AllowGet);
}
The first time that I hit the submit button, the code here goes until the db.CLIENTES.Add part of the code - then it doesn't run the DB.SAVECHANGES() nor the rest of the code here. The second time it works like a charm. The problems just happen on the first submit hit.
As the return of the controller doesn't happens properly, the final part of the Javascript code does not run as well. This part:
window.location.href = "CadastroPet";
return false;
Can anyone help me?
(All the view is inside this div
<div ng-controller="AdicionarClientesController">
)
UPDATE
I removed the TYPE of the submit button and put the simple button type. It seems to be working now. How can I submit my form then?
First,as per EF best practice, try to wrap the db operation in using() { } block. Thus your controller lokks like
[HttpPost]
public JsonResult AdicionarCliente(string NomeCliente, string Telefone1Cliente)
{
var Clientes = new CLIENTES();
using(var db = new RexsoftEntities())
{
var _Clientes = new CLIENTES()
{
NOME = NomeCliente,
TELEFONE1 = Telefone1Cliente
};
db.CLIENTES.Add(_Clientes);
db.SaveChanges();
Clientes = db.CLIENTES.ToList();
}
return Json(Clientes, JsonRequestBehavior.AllowGet);
}
Secondly, in javascript side, you are using angularjs. window.location.href will not work in angular(see this and this). You have to use $window service (source: using angularjs $window service) or $location service (source: using angularjs $location service). Also avoid using return false;.
In your case the below will work.
promisse.then(function () {
$location.path('/CadastroPet');
});
I removed the TYPE of the submit button and put the simple button type. It seems to be working now.
I created another way to validate my form using the same js script that I mentioned. If the criterias wasn't met, i would return a message and a return false statement.
In this scenario I'm using the ui-bootstrap typeahead to capture an object from an external api. Using the select callback I'm getting that object and have the results set in a separate function within my controller.
The issue is that I want to take those results and send them off to a separate api with a click function I already have set up. My question is how do i get the results of the type-ahead into the click function to post? The user flow is as follows.
<div>
<input type="text" placeholder="Find A Game"
typeahead-on-select="setGames($item)"
ng-model="asyncSelected"
typeahead="test.name for test in getGames($viewValue)"
typeahead-loading="loadingLocations" typeahead-min-length="3"
typeahead-wait-ms="500" typeahead-select-on-blur="true"
typeahead-no-results="noResults">
</div>
<div ng-show="noResults">
No Results Found
</div>
<button ng-disabled="!asyncSelected.length"
ng-click="addtodb(asyncSelected)">Add To Database</button>
As you can see the label is set to the items name and this works fine. When the user selects the name I then use typeahead-on-select="setGames($item)" to send off the entire object to its own funtion. From there I want to take the object and pass it to another function that you can see within the button tags ng-click. I currently have it passing the model, but what I really want is to pass the entire object within $item from the select event. So far my controller looks like this:
angular.module('2o2pNgApp')
.controller('GiantCtrl', function ($scope, $http, TermFactory, $window, SaveFactory) {
$scope.getGames = function(val) {
return $http.jsonp('http://www.example.com/api/search/?resources=game&api_key=s&format=jsonp&limit=5&json_callback=JSON_CALLBACK', {
params: {
query: val
}
}).then(function(response){
return response.data.results.map(function(item){
return item;
});
});
};
$scope.setGames = function (site) {
var newsite = site;
};
$scope.addtodb = function (asyncSelected, newsite) {
TermFactory.get({name: asyncSelected}, function(data){
var results = data.list;
if (results === undefined || results.length === 0) {
SaveFactory.save({vocabulary:'5', name:newsite.name, field_game_id:newsite.id}, function(data) {
$window.alert('All Set, we saved '+asyncSelected+' into our database for you!')
});
} else {
// do stuff
});
}
});
No matter what I do I cant seem to pass the entire $item object into this click function to post all the info i need.
Via New Dev in Comments:
$item is only available locally for typeahead-on-select... you can
either assign it to some model within your controller, or, in fact,
make the model of typeahead to be the item: typeahead="test as
test.name for test in getGames($viewValue)" – New Dev
I'm strugling with a jquery script inside a cshtml page. For short my question is how to use a var inside a # statement in a cshtml page?
below an example of what I'm trying:
<select id="DefaultText">
<option value="-1">-- select --</option>
#foreach( var d in Model.DefaultTexts )
{
<option value="#d.Id" >#d.Name</option>
}
</select>
<script type="text/javascript">
$('#DefaultText').change(function () {
var id = parseInt($('#DefaultText :selected').val());
var text = #Model.DefaultTexts.First( t => t.Id == id );
$('#CustomProductText').val(text);
});
</script>
I can't reach the var id. It's out of scope. I've also tryed it with a for loop and a if statement. But in the if statement I get the same error: out of scope.
The full story is this:
On my page I've a dropdown list. The items to select are short names for default text parts. Based on the id or name, I want to show the default text part in a textbox.
#CustomProductText is my textbox where the content should be placed (code not posted).
I've also tryed it with #: and statement but that did not work.
What am I doing wrong or maybe its not even possible what I'm trying to do.
As an alternative I've added a action to my controller to get the text form there. Below the code:
<script type="text/javascript">
$('#DefaultText').change(function () {
var id = parseInt($('#DefaultText :selected').val());
$.post("Categories/GetDefaultText", { Id: id }, function (data) {
alert(data);
});
//$('#CustomProductText').val(text);
});
</script>
controller code:
[HttpPost]
public ActionResult GetDefaultText(int id)
{
using( var context = new MyContext() )
{
var text = context.DefaultText.First( d => d.Id == id ).Text;
return this.Content( text );
}
}
This doesn't work. The action doesn't get hit in debug mode.
regards,
Daniel
The $.post that is not working for you, you should prefix the url with / sign and it will be hit as expected:
$.post("/Categories/GetDefaultText", { Id: id }, function (data) {
alert(data);
});
As for the razor solution, you can't use javascript variables in the razor code as it's not a scripting language. What razor does is simply rendering the strings (be it html or javascript or anything) into the page.
To do what you want you either need to request the server to pass the text to your page or render all the texts you have in the page and then access this rendered content in your javascript.
The client will choose an item from a dropDown list, this newly selected value will then be used to find assets linked to that selected item, these assets will then be loaded into the listBox.
This sounds simple enough, and I'm aware I could use a partial View but it seems overkill for just updating one component on a form.
Any
I've done this in MVC 1.0 myself. I used an onchange on the first drop down which called an action using the value selected. That action returned a JSON result. The jQuery script which called that action then used the JSON to fill the second drop down list.
Is that enough explanation, or would you like help writing the javascript, the action, or both?
Inside your view:
<%= this.Select("DDLName").Attr("onchange", "NameOfJavascriptFunction();") %>
<%= this.MultiSelect("ListBoxName") %>
The javascript will look like this:
function NameOfJavascriptFunction() {
var ddlValue = $("DDLName").val();
jQuery.ajax({
type: 'GET',
datatype: 'json',
url: '/Controller/Action/' + dValue,
success: updateMultiSelect
});
}
function updateMultiSelect(data, status) {
$("#ListBoxName").html("");
for(var d in data) {
$("<option value=\"" + data[d].Value + "\">" + data[d].Name + "</option>">).appendTo("#ListBoxName");
}
}
Finally, the action is something like this (put this in the controller and action from the first javascript):
public ActionResult Action(int id) //use string if the value of your ddl is not an integer
{
var data = new List<object>();
var result = new JsonResult();
this.SomeLogic.ReturnAnEnumerable(id).ToList().ForEach(foo => data.Add(new { Value = foo.ValueProperty, Name = foo.NameProperty }));
result.Data = data;
return result;
}
Feel free to ask follow up questions if you need any more explanation.