How to update a label from a postback in MVC3/Razor - asp.net-mvc-3

MVC/Razor/Javascript newbie question:
I have a MVC3/Razor form where the use can select a single product from a drop down list.
<div class="editor-label">
Product
</div>
<div class="editor-field">
#Html.DropDownList("ProductID", (IEnumerable<SelectListItem>)ViewBag.Products, "--Select One--")
#Html.ValidationMessageFor(model => model.ProductID)
</div>
What I then want is to display the price of the selected product on a label just below the drop down list (model property name is Amount).
This should be pretty easy, but I am pretty new at Razor, and know almost nothing about Javascript, so I would appreciate any verbose explanations of how do do it, and how it all hangs together.

Add a div/span under the Dropdown .
#Html.DropDownList("ProductID", (IEnumerable<SelectListItem>)ViewBag.Products, "--Select One--")
<div id="itemPrice"></div>
and in your Script, make an ajax call to one of your controller action where you return the price.
$(function(){
$("#ProductId").change(function(){
var val=$(this).val();
$("#itemPrice").load("#Url.Action("GetPrice","Product")", { itemId : val });
});
});
and have a controller action like this in your Product controller
public string GetPrice(int itemId)
{
decimal itemPrice=0.0M;
//using the Id, get the price of the product from your data layer and set that to itemPrice variable.
return itemPrice.ToString();
}
That is it ! Make sure you have jQuery loaded in your page and this will work fine.
EDIT : Include this line in your page to load jQuery library ( If it is not already loaded),
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>

The Amount isn't available to your view when the user selects a product (remember the page is rendered on the server, but actually executes on the client; your model isn't available in the page on the client-side). So you would either have to render in a JavaScript array that contains a lookup of the amount based on the product which gets passed down to the client (so it's available via client-side JavaScript), or you would have to make a callback to the server to retrieve this information.
I would use jQuery to do this.
Here's a simple example of what the jQuery/Javascript code might look like if you used an array.
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script>
$(document).ready(function() {
// This code can easily be built up server side as a string, then
// embedded here using #Html.Raw(Model.NameOfPropertyWithString)
var list = new Array();
list[0] = "";
list[1] = "$1.00";
list[2] = "$1.25";
$("#ProductID").change(displayAmount).keypress(displayAmount);
function displayAmount() {
var amount = list[($(this).prop('selectedIndex'))];
$("#amount").html(amount);
}
});
</script>
<select id="ProductID" name="ProductID">
<option value="" selected>-- Select --</option>
<option value="1">First</option>
<option value="2">Second</option>
</select>
<div id="amount"></div>
You'll want to spend some time looking at the docs for jQuery. You'll end up using it quite a bit. The code basically "selects" the dropdown and attaches handlers to the change and keypress events. When they fire, it calls the displayAmount function. displayAmount() retrieves the selected index, then grabs the value out of the list. Finally it sets the HTML to the amount retrieved.
Instead of the local array, you could call your controller. You would create an action (method) on your controller that returned the value as a JsonResult. You would do a callback using jquery.ajax(). Do some searching here and the jQuery site, I'm sure you'll find a ton of examples on how to do this.

Related

Mixing Alpine.js with 'static' serverside markup, while getting the benefits of binding, etc

I'm new to Alpine and struggling to wrap my head around how to make a scenario like this work:
Let's say I have a serverside built page, that contains some buttons, that represent newsletters, the user can sign up to.
The user might have signed up to some, and we need to indicate that as well, by adding a css-class, .i.e is-signed-up.
The initial serverside markup could be something like this:
<button id='newsletter-1' class='newsletter-signup'>Newsletter 1</button>
<div>some content here...</div>
<button id='newsletter-2' class='newsletter-signup'>Newsletter 2</button>
<div>more content here...</div>
<button id='newsletter-3' class='newsletter-signup'>Newsletter 3</button>
<div>and here...</div>
<button id='newsletter-4' class='newsletter-signup'>Newsletter 4</button>
(When all has loaded, the <button>'s should later allow the user to subscribe or unsubscribe to a newsletter directly, by clicking on one of the buttons, which should toggle the is-signed-up css-class accordingly.)
Anyway, then I fetch some json from an endpoint, that could look like this:
{"newsletters":[
{"newsletter":"newsletter-1"},
{"newsletter":"newsletter-2"},
{"newsletter":"newsletter-4"}
]}
I guess it could look something like this also:
{"newsletters":["newsletter-1", "newsletter-2", "newsletter-4"]}
Or some other structure, but the situation would be, that the user have signed up to newsletter 1, 2 and 4, but not newsletter 3, and we don't know that, until we get the JSON from the endpoint.
(But maybe the first variation is easier to map to a model, I guess...)
Anyway, I would like to do three things:
Make Alpine get the relation between the model and the dom elements with the specific newsletter id (i.e. 'newsletter-2') - even if that exact id doesn't exist in the model.
If the user has signed up to a newsletter, add the is-signed-up css-class to the corresponding <button> to show its status to the user.
Bind to each newsletter-button, so all of them – not just the ones, the user has signed up to – listens for a 'click' and update the model accordingly.
I have a notion, that I might need to 'prepare' each newsletter-button beforehand with some Alpine-attributes, like 'x-model='newsletter-2', but I'm still unsure how to bind them together when Alpine has initialising, and I have the data from the endpoint,
How do I go about something like this?
Many thanks in advance! 😊
So our basic task here is to add/remove a specific item to/from a list on a button click. Here I defined two component: the newsletter component using Alpine.data() creates the data (subs array), provides the toggling method (toggle_subscription(which)) and the checking method (is_subscribed(which)) that we can use to set the correct CSS class to a button. It also handles the data fetching in the init() method that executes automatically after the component is initialized. I have also created a save method that we can use to send the subscription list back to the backend.
The second component, subButton with Alpine.bind() is just to make the HTML code more compact and readable. (We can put each attribute from this directly to the buttons.) So on click event it calls the toggle_subscription with the current newsletter's key as the argument to add/remove it. Additionally it binds the bg-red CSS class to the button if the current newsletter is in the list. For that we use the is_subscribed method defined in our main component.
.bg-red {
background-color: Tomato;
}
<script src="https://unpkg.com/alpinejs#3.x.x/dist/cdn.min.js" defer></script>
<div x-data="newsletter">
<button x-bind="subButton('newsletter-1')">Newsletter 1</button>
<button x-bind="subButton('newsletter-2')">Newsletter 2</button>
<button x-bind="subButton('newsletter-3')">Newsletter 3</button>
<button x-bind="subButton('newsletter-4')">Newsletter 4</button>
<div>
<button #click="save">Save</button>
</div>
</div>
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('newsletter', () => ({
subs: [],
init() {
// Fetch list of subscribed newsletters from backend
this.subs = ['newsletter-1', 'newsletter-2', 'newsletter-4']
},
toggle_subscription(which) {
if (this.subs.includes(which)) {
this.subs = this.subs.filter(item => item !== which)
}
else {
this.subs.push(which)
}
},
is_subscribed(which) {
return this.subs.includes(which)
},
save() {
// Send this.sub to the backend to save active state.
}
}))
Alpine.bind('subButton', (key) => ({
'#click'() {
this.toggle_subscription(key)
},
':class'() {
return this.is_subscribed(key) && 'bg-red'
}
}))
})
</script>

Form select box in Backbone Marionette

I'm trying using Backbone.Marionette to build an application. The application gets its data through REST calls.
In this application I created a model which contains the following fields:
id
name
language
type
I also created an ItemView that contains a complete form for the model. The template I'm using is this:
<form>
<input id="model-id" class="uneditable-input" name="id" type="text" value="{{id}}"/>
<input id="model-name" class="uneditable-input" name="name" type="text" value="{{name}}" />
<select id="model-language" name="language"></select>
<select id="model-type" name="type"></select>
<button class="btn btn-submit">Save</button>
</form>
(I'm using Twig.js for rendering the templates)
I am able to succesfully fetch a model's data and display the view.
What I want to do now is populate the select boxes for model-language and model-type with options. Language and type fields are to be restricted to values as a result from REST calls as well, i.e. I have a list of languages and a list of types provided to me through REST.
I'm contemplating on having two collections, one for language and one for type, create a view for each (i.e. viewLanguageSelectOptions and viewTypeSelectOptions), which renders the options in the form of the template I specified above. What I am not sure of is if this is possible, or where to do the populating of options and how to set the selected option based on data from the model. It's not clear to me, even by looking at examples and docs available, which Marionette view type this may best be realized with. Maybe I'm looking in the wrong direction.
In other words, I'm stuck right now and I'm wondering of any of you fellow Backbone Marionette users have suggestions or solutions. Hope you can help!
Create a view for a Select in my opinion is not needed in the scenario that you are describing, as Im assuming that your languages list will not be changing often, and the only porpouse is to provide a list from where to pick a value so you can populate your selects in the onRender or initializace function of your view using jquery.
you can make the calls to your REST service and get the lists before rendering your view and pass this list to the view as options and populate your selects on the onRender function
var MyItemView = Backbone.Marionette.ItemView.extend({
initialize : function (options) {
this.languages = options.languages;
this.typeList = options.typeList;
},
template : "#atemplate",
onRender : function () {
this.renderSelect(this.languages, "#languagesSelect", "valueofThelist");
this.renderSelect(this.typeList, "#typesSelect", "valueofThelist")
},
renderSelect :function (list, element, value) {
$.each(list, function(){
_this.$el.find(element).append("<option value='"+this[value]+"'>"+this[value]+"</option>");
});
}
})
var languagesList = getLanguages();
var typeList = getTypesList();
var myItemView = new MyItemView({languages:languagesList,typeList :typeList });
Hope this helps.

Select box populated dynamically with AJAX doesn't post on form submission

This is my first attempt at chaining select boxes in a web form using ajax and I I'm obviously missing something. I'm simply at a loss for what that is, exactly. Here is my issue:
A user selects a Country from one select box and an ajax request is made and options (containing names of States and Territories) are returned to a select box below. While the options are returned into the form select field, the user-selected option is NOT sent when the form is submitted.
Here is the code I've cooked up:
<script type="text/javascript">
jQuery(document).ready(function($){
$("select#state").attr("disabled","disabled");
$("select#country").change(function(){
$("select#state").attr("disabled","disabled");
$("select#state").html("<option>Loading States...</option>");
var id = $("select#country option:selected").attr('value');
$.post("http://example.com/terms.php", {id:id}, function(data){
$("select#state").removeAttr("disabled");
$("select#state").html(data);
});
});
});
</script>
You can see the live example here (see the Country/State section):
http://shredtopia.com/add/
Any ideas what is needed to get this working?
As far i can see, the user input is sent
input_32 79
input_29 alberta
Being 79 the country canada and alberta the state.
<select tabindex="11" class="medium gfield_select" id="input_1_32" name="input_32"></select>
<select tabindex="12" class="medium gfield_select" id="input_1_29" name="input_29" disabled=""></select>
Maybe i misunderstood the issue?
Try .live( eventType,handler )
Description: Attach a handler to the event for all elements which match the current selector, now and in the future.
http://api.jquery.com/live/
Add to your code and try it~
$('select#state').live('change', function() {
var id = $("select#state option:selected").attr('value');
alert(id);
});
Or try this:
add a hidden in form:
<input type="hidden" id="hiddenValue">
alter your select#state like this:
<select onchange='innerValue(this.options[this.options.selectedIndex].value)'></select>
and create a javascript function
function innerValue(value){
$("#hiddenValue").val(value)
}
then,click submitbutton,$("#hiddenValue").val() is you need
$("#submitbutton").click(function(){
alert($("#hiddenValue").val())
})
but,I think this is not the best solution...

change multiple hidden input values using a drop-down box and jquery

Hey, I'm having some trouble with this problem, and I don't even know where to start.
I'm using foxycart for an ecommerce website I'm building for my girlfriend, so sending values to the "cart" is limited to the input names foxycart is looking for.
IE; name, price, product_sku.
I have a tiny CMS backend that allows you to add different sizes, sku's for those sizes and a different price for that size.
So, being that I'm using foxycart, I need hidden inputs to send the values to the cart.
<input type="hidden" name="name" value="Test" />
<input type="hidden" id="price" name="price" value="19.99" />
<input type="hidden" id="product_sku" name="product_sku" value="sku3445" />
<input type="hidden" id="product_id" name="product_id" value="123" />
This works good. sends the name, price and sku to the cart.
I've made a drop down box that lists the different sizes/prices related to that product. I've set it up so that selecting a different size changes the price:
<select id="single" name="options" />
<option name="option_price" value="19.99">Default - $19.99</option>
<option name="option_price" value="18.99">Test Size: 18.99</option>
</select>
function displayVals() {
var singleValues = $("#single").val();
("#item_price").html(singleValues);
$("#price").val(singleValues);
}
$("select").change(displayVals);
displayVals();
This works too, send the price selected to a div and the hidden price input(so you can see the new purchase price) and to the cart(so the cart is showing the price of the product you want to purchase)
And now for the question:
How do I set this up so that selecting a different size/price will change the hidden inputs so that the product_sku, and size name are updated along with the price?
I'm thinking I have to use some Jquery.ajax() call, but have no idea...
Would this work?:
Jquery:
$(document).ready(function(){
$("form#get_stuff").change(function() {
var product_id= $('#product_id').attr('value');
$.ajax({
type: "POST",
url: get_stuff.php,
data: "product_id="+product_id,
success: function(data){
$('#product_inputs').html(data);
}
});
return false;
});
});
the 'data' being:
from the php page?
This is my first foray into Jquery ajax, so I really have no idea.
Edit:
Sorry, I just read this over and it's kind of confusing....
Here is the workflow I'm trying to accomplish:
Page loads:
using php, echo product name, price, sku. (This is the default)
Drop-box change:
using jquery, dynamically change the hidden inputs with new information based off the product_id, and the size selected from the drop-box (Update 4 hidden inputs based off the value from one value from a select menu)
Instead of using AJAX when the select box changes, you can also load the SKU and product ID when the page loads and add them as data on the option tags. One way to do this is to add them as classes like so:
<select id="single" name="options">
<option name="option_price" class="sku3445 id123" value="19.99">Default - $19.99</option>
<option name="option_price" class="sku3554 id321" value="18.99">Test Size: 18.99</option>
</select>
Then using a little RegEx you can extract these values from the selected option in your change() function and update the hidden inputs accordingly:
function displayVals() {
var $single = $('#single'),
singleValues = $single.val(),
singleClasses = $single.find('option:selected').attr('class'),
singleSKU = singleClasses.match(/\bsku\d+\b/)[0],
singleID = singleClasses.match(/\bid\d+\b/)[0].replace('id','');
$("#item_price").html(singleValues);
$("#price").val(singleValues);
$('#product_sku').val(singleSKU);
$('#product_id').val(singleID);
}
$("select").change(displayVals);
displayVals();
Here is a working example →
Using Ajax is the way to go. When the dropdown value changes, you will want to trigger the Ajax call to a PHP method, which I assume would query a backend database for necessary information using the dropdown value as a parameter, then return that information to populate the hidden fileds. All these steps should happen in your Ajax call.

Mootools 1.2.4 delegation not working in IE8...?

So I have a listbox next to a form. When the user clicks an option in the select box, I make a request for the related data, returned in a JSON object, which gets put into the form elements. When the form is saved, the request goes thru and the listbox is rebuilt with the updated data. Since it's being rebuilt I'm trying to use delegation on the listbox's parent div for the onchange code. The trouble I'm having is with IE8 (big shock) not firing the delegated event.
I have the following HTML:
<div id="listwrapper" class="span-10 append-1 last">
<select id="list" name="list" size="20">
<option value="86">Adrian Franklin</option>
<option value="16">Adrian McCorvey</option>
<option value="196">Virginia Thomas</option>
</select>
</div>
and the following script to go with it:
window.addEvent('domready', function() {
var jsonreq = new Request.JSON();
$('listwrapper').addEvent('change:relay(select)', function(e) {
alert('this doesn't fire in IE8');
e.stop();
var status= $('statuswrapper').empty().addClass('ajax-loading');
jsonreq.options.url = 'de_getformdata.php';
jsonreq.options.method = 'post';
jsonreq.options.data = {'getlist':'<?php echo $getlist ?>','pkey':$('list').value};
jsonreq.onSuccess = function(rObj, rTxt) {
status.removeClass('ajax-loading');
for (key in rObj) {
status.set('html','You are currently editing '+rObj['cname']);
if ($chk($(key))) $(key).value = rObj[key];
}
$('lalsoaccomp-yes').set('checked',(($('naccompkey').value > 0)?'true':'false'));
$('lalsoaccomp-no').set('checked',(($('naccompkey').value > 0)?'false':'true'));
}
jsonreq.send();
});
});
(I took out a bit of unrelated stuff). So this all works as expected in firefox, but IE8 refuses to fire the delegated change event on the select element. If I attach the change function directly to the select, then it works just fine.
Am I missing something? Does IE8 just not like the :relay?
Sidenote: I'm very new to mootools and javascripting, etc, so if there's something that can be improved code-wise, please let me know too..
Thanks!
Element Delegation will not work on field elements (input/select/textarea) in IE's.

Resources