use jquery variable in # block razor - javascript

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.

Related

Unable to read value from document

I'm a novice in MVC, Below is my code
I am unable to read the value of an ID and use that in an decision statement, I am getting "The name "Text" does not exist in current context", I need to work on the if statement based on the value I get from my document.getElementById
#{
var grid = new WebGrid(Model.Abc, canPage: true, canSort: true, rowsPerPage: 50);
}
#{
var gridColumnsNew = new List<WebGridColumn>();
gridColumnsNew.Add(grid.Column("Details", header: "Id"));
<text>
var obj = document.getElementById("NextAction").value;
</text>
if (#text.obj == "Start")
{
gridColumnsNew.Add(grid.Column("Temp"));
}
}
Try using
document.getElementsByName("NextAction").value;
I have seen in my case that Blazor changes Id to name.
Note: I am using DevexpressBlazor
Did you checked if you are able to see on the html generated that ID?
If yes, Did you have any JS error before?
Looks like the ID not was generated or the place where you are run the getElementById don't have visibility to your specific code.
You are mixing razor syntax and javascript. The line var obj = document.getElementById("NextAction").value; is javascript and should go inside <script> tag. You can't call javascript functions from razor code.
Solution:
Assuming you have a controller named GridController.cs and a view named Grid.cshtml. Inside your controller add a new HttpPost action:
[HttpPost]
public IActionResult NextAction(string nextAction)
{
ViewData["NextAction"] = nextAction;
return View("Grid");
}
Inside the view add a form that posts the nextAction value to the controller:
<form asp-action="NextAction" asp-controller="Grid">
<input type="hidden" value="Start" name="nextAction" />
<button type="submit">Start</button>
</form>
The controller added the NextAction value in the ViewData dictionary so now the view can access it:
#{
var gridColumnsNew = new List<WebGridColumn>();
gridColumnsNew.Add(grid.Column("Details", header: "Id"));
if (ViewData["NextAction"] == "Start")
{
gridColumnsNew.Add(grid.Column("Temp"));
}
}
You are getting that error because you are using #text.obj. In Razor, once you attached # before any identifier, it considers it a C# or VB variable.
Since we don't have your entire page, you may need to clarify where the source of the NextAction. It will be helpful. See a sample of something similar.
#if(item.Ward == "start")
{
gridColumnsNew.Add(grid.Column("Temp"));
}
The item is from the model I am iterating to form the grid.

How do I populate a list field in a model from javascript?

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.

Tokeninput save new tag to database

I am trying to save a new tag entered in input field that was not there in database and want to save that created tag on form submit. Here is my controller which is sending the autocomplete list to the tokeninput input field:
def tags = {
def foundTags = Tag.findAllByTagnameIlike("${params.q}%")
def output = []
foundTags.each {
output.add([id: it.id, name: it.tagname]) // assumes Tag has an id field exposed
}
if(output.size()==0){
def c = Tag.createCriteria()
def maxId = c.get {
projections {
max('id')
}
}
output.add([id:(maxId+1),name:params.q])
}
render output as JSON
}
My jQuery script is:
<script type="text/javascript">
$(document).ready(function () {
$("#my-text-input").tokenInput("${createLink(controller: 'product', action: 'tags')}",{theme: "facebook",allowFreeTagging:"true"});
});
</script>
Now when I submit the form, i get params.tags as the ids of those newly entered tags in the input field.But actually these IDs do not exist and are created by output.add([id:(maxId+1),name:params.q]) just for the reason that tokeninput requires it to be there.
So how do i get the tag names in the params.tags instead of the ids? Infact i require something like this map ["id1":"tagname1","id2":"tagname2".....]. So how do i get the actual tagname instead of the id fields in the server side action which persists the form params?
Make use of the tokenValue parameter during set up.
e.g. In jQuery
<script type="text/javascript">
$(document).ready(function () {
$("#my-text-input").tokenInput("${createLink(controller: 'product', action: 'tags')}",{
theme: "facebook",
allowFreeTagging:"true",
tokenValue:"name"
});
});
</script>
This will submit an array of names instead of IDs. If you need both name's and id's, you're probably best adding a custom attribute to each token through an onAdd parameter, and then setting that to the tokenValue.

How to display value of a ViewBag in my view with a JS function?

I want to display the data from a ViewBag in my View with Javascript. Here is my code.
View
<span id='test'></span>
Javascript
function myFunction()
{
$('#test').text('#ViewBag.Test');
}
When myFunction() is called I get the text #ViewBag.Test but not his value. How can I fix this ?
You need to place your JavaScript which takes the #ViewBag.Test value in a page which is interpreted by the Razor view engine. My guess is that this is currently not the case.
If you want to keep your javascript codebase separate from the view (which is entirely reasonable) you can use a global variable:
// in the view:
var testText = '#ViewBag.Test';
// in external js
function myFunction() {
$('#test').text(window.testText);
}
Alternatively, you can use a data-* attribute:
<span id='test' data-text="#ViewBag.Test"></span>
// in external js
function myFunction() {
$('#test').text(function() {
return $(this).data('text');
});
}
What you should be ideally doing is passing the data to the view with a view model. Have a property to store that value you want to pass. For example. Let's think about a page to show the customer details and you want to get the last name in your javascript variable.
Your GET action method
public ActionResult View(int id)
{
var vm=new CustomerViewModel();
vm.LastName="Scott"; // You may read this from any where(DAL/Session etc)
return View(vm);
}
and in your view which is strongly typed to your view model.
#model CustomerViewModel
<div>
Some Html content goes here
</div>
<script type="text/javascript">
var lastName="#Model.LastName";
//Now you can use lastName variable
</script>
EDIT : (As per the question edit) To show the content on some event (ex : some button click), Store the value somewhere initially and then read it as needed and set it wherever you want.
#model CustomerViewModel
<div>
<span id="content"></span>
#Html.HiddenFor(s=>s.LastName)
<input type="button" id="btnShow" value="Show content" />
</div>
<script type="text/javascript">
$(function(){
$("btnShow").click(function(e){
$("#content").html($("#LastName").val());
});
});
</script>
Firstly make sure your ViewBag.Test does got a value, then use a div tag instead of a span and add the following code:
<script type="text/javascript">
$(document).ready(function () {
StartRead();
});
function StartRead() {
document.getElementById("test").innerHTML = '#ViewBag.Test';
}
</script>

Mixing JavaScript and Scala in a Play template

I'm not sure how this is done. I could hard code the route I'm trying to use, but I'd like to do this the right way.
I have a dropdown that needs to load a new page on change. Here's basically how I'm trying to do it (I've tried a few variations of this):
#getRoute(value: String) = #{
routes.Accounts.transactions(Long.valueOf(value))
}
<script type="text/javascript">
$(function() {
$("select[name='product']").change(function() {
location.href = #getRoute($(this).val());
}).focus();
$('a.view.summary').attr('href', "#routes.Accounts.index()" + "?selectedAccountKey=" + $('select[name=product]').val());
});
</script>
This produces a identifier expected but 'val' found exception. I also tried surrounding it in quotes, but that causes a [NumberFormatException: For input string: "$(this).val()"]
So how the heck do I insert a value from JavaScript into a Scala function?
Edit
Here's my solution, inspired by the accepted answer. This dropdown is defined in a tag that's made for reuse by different components, and the base URL is different for each component. The way to achieve this was to pass a function that generates a URL based on an account key into the dropdown:
#(accountList: List[models.MemberAccount],
selectedAccountKey: Long,
urlGenerator: (Long) => Html
)
<select name="product">
#for(account <- accountList) {
#if(account.accountKey == selectedAccountKey) {
<option selected="selected" value="#urlGenerator(account.accountKey)">#account.description (#account.startDate)</option>
} else {
<option value="#urlGenerator(account.accountKey)">#account.description (#account.startDate)</option>
}
}
</select>
<script type="text/javascript">
$(function() {
$('select[name=product]').change(function() {
location.href = $(this).val();
});
});
</script>
Then you can define a function like this to pass in:
#transactionsUrl(memberAccountKey: Long) = {
#routes.Accounts.transactions(memberAccountKey)
}
#accountsDropdown(transactionDetails.getMemberAccounts(), transactionDetails.getMemberAccountKey(), transactionsUrl)
You need a way of storing all URLs in the page, e.g.
<option value="#routes.Accounts.transactions(id)">Display</option>
Then onChange, you can:
$("select[name='product']").change(function() {
location.href = $(this).val();
});

Categories