Set selected option of type model in dropdownlist with knockout - asp.net-mvc-3

I'm trying to load the saved option from server side with knockout, please see below the general idea,
I have the following classes in javascript:
funcion Request() {
this.Id = ko.observable('');
this.Name = ko.observable('');
this.Form = ko.obsevable('');
}
function Form() {
this.Id = ko.observable('');
this.Name = ko.observable('');
}
this is my viewModel
function RequestViewModel() {
var self = this;
self.Request = new Request();
*self.Request.Form = new Form();*
}
I can save the Form without problem, but when I try to load the Form field saved into the database, the binding doesn't function.
If anybody have ever had the same problem, please let me know How can I fix it?

You form is an observable. When setting an observable, you should call it as a method, and set parse the value as a parameter. Something like this:
function RequestViewModel() {
var self = this;
self.Request = new Request();
self.Request.Form(new Form());
}
If you have loaded the form from the database, it should look something like this:
self.Request.Form(myLoadedForm);

The answer by aaberg is correct, but if you're saying you need to load them all at once, I recommend you use the knockout mapping plugin to automate this: http://knockoutjs.com/documentation/plugins-mapping.html
Your call would look something like this:
ViewModel = ko.mapping.fromJS(requestFromServer);

Related

Xamarin: set labelText with an instance of ViewModel isnt updating UI

I defined a Label in Xaml
<Label Text="{Binding DeviceGuid}"/>
set the BindingContext inside my Page
BindingContext = new BluetoothViewModel();
and wrote the code for the getter and setter in the ViewModel
private string _deviceGuid;
public string DeviceGuid
{
get
{
return _deviceGuid;
}
set
{
if (_deviceGuid != value)
{
_deviceGuid = value;
OnPropertyChanged();
}
}
}
So thats the simple Thing :). The Binding works if I change the value inside the ViewModel.
Now here it comes:
There are some Backgroundtasks (or just other classes) that, in my opinion, should have Access to that property and if they will write it, the UI should update automatically.
I think its bad practice but I dont know how to realise it different.
I´ve already tried to create another instance of the viewmodel like
BluetoothViewModel a = new BluetoothViewModel();
a.DeviceGuid = "test";
Its calling the OnPropertyChanged() but isnt updating the UI ...
Thanks for your help in advance.
When you do this:
BluetoothViewModel a = new BluetoothViewModel();
a.DeviceGuid = "test";
You are creating another instance of the viewmodel that is not the one in your BindingContext.
Do this instead:
public BluetoothViewModel viewmodel;
BindingContext = viewmodel= new BluetoothViewModel();
And then:
viewmodel.DeviceGuid = "test";
The reason it must be happening is that you are not making these changes in the MainThread which is the thread responsible for making changes on the UI.
Do something like below where you change the property data:
Device.BeginInvokeOnMainThread(() => {
DeviceGuid="New string"; });
Update
What you should be doing is using the BindingContext and creating a new instance so your variable 'a' should look something like below
private BluetoothViewModel viewmodel;
BindingContext = viewmodel= new BluetoothViewModel ();
And then do this
viewmodel.DeviceGuid="New string";

Mocking Request.QueryString for Unit Tests and asserting against views

I have the following controller:
public class ResetController : Controller
{
//
// GET: /Reset/
private Models.ResetModel rm = new Models.ResetModel();
public ActionResult Index()
{
//Check that this has a query string that is containing in the database
//and has been done in the last 30 mins.
string qString = Request.QueryString["v"].ToString();
//if this is a good querystring
if (rm.CheckQString(qString))
return View();
else
return View("Index", "Home");
}
I now need to create a unit test to ensure that if the Request.QueryString value is found in the database then the appropriate view is returned but I am unable to do so. Here is my attempt at a test to check this:
[TestMethod()]
public void IndexTest()
{
ResetController target = new ResetController();
var request = new Mock<HttpRequestBase>();
request.SetupGet(r => r.QueryString).Returns(HttpUtility.ParseQueryString("?v=0ocIqhOQkrBaCXRO96E4B5HcOCYgMfJYOpRdNU/yIEUmH2szuXXKU51Td6NzRxlk"));
var result = target.Index() as ActionResult;
Assert.IsNotNull(result);
}
Can someone please help me with suggestions to ensure that this controller is fully tested?
Thanks
This is a late answer, but in the event that someone comes along this post in the future... Refer to this post how would I mock a querystring
The goal is to isolate the test so that it does not depend on the QueryString result from the database, but rather a provided value. To do this in Moq use the SetupGet method after creating a Mock Context. Hope this helps someone!
I would suggest you pass the model as a dependency to the controller. Then you can mock it as well in the unit test to isolate your controller logic from the model's CheckQString implementation logic.
I'm not sure though if I understand your problem correctly.
The good case might then look like this. Of course you would need to check if the correct view was returned.
[TestMethod()]
public void IndexTest()
{
const string query = "some query";
Models.ResetModel rm = new Mock<Models.ResetModel>();
rm.Setup(m => m.CheckQString(query)).Returns(true);
ResetController target = new ResetController(rm.Object);
var request = new Mock<HttpRequestBase>();
request.SetupGet(r => r.QueryString).Returns(HttpUtility.ParseQueryString("?v=" + query));
var result = target.Index() as ActionResult;
Assert.IsNotNull(result);
}

How to update knockout model in mvc3 app

I've been playing with MVC3 with KnockoutJs for a few weeks and I've been wondering about something
say I have an mvc action which returns a simple list
public ActionResult AddFoos()
{
List<Foo> funds = new List<Foo>();
.. etc
return Json(funds.ToList(), JsonRequestBehavior.AllowGet);
}
which is then passed into the view model
var viewModel = {
fooChocies: ko.mapping.fromJS([]),
fooOptions: ko.mapping.fromJS([]),
loadInitialData: function () {
ko.mapping.fromJS(serverData, dataMappingOptions, viewModel.fooOptions);
},
};
In my type Foo I also have properties that show or hide ui elements
var Foo = function (data, preselect) {
var self = this;
self.id = ko.observable(data.id);
self.Name = ko.observable(data.Name);
self.number = ko.observable('');
self.showProducts = ko.observable(false); <---
self.displayBigPanel = ko.observable(false); <---
}
My approach so far as been to dynamically create elements of the form
which passes through the ModelBinder and creates a List< Foo > as a parameter for controller action.
Finally the question...
When the user navigates back to this page I need to restore the UI with the fooChoices the user made.
It seems I have 2 choices with rebuilding the user selections (both via extension methods)
Use raw json as seen by
ko.toJSON(viewModel.fooChoices))
which in addition to basic model properties also provides info on hiding and displaying UI elements,
sb.Append("viewModel.fooCghoices= ko.mapping.fromJS(" + json + ");");
sb.Append("ko.applyBindings(viewModel);");
return new HtmlString(sb.ToString());
thus sending client ui info to the server and back
or
Manipulate the ViewModel directly in effect simulating the user actions
sb.Append("viewModel.fooChoices.push(new Foo(1509));");
sb.Append("viewModel.fooChoices()[0].selectedSubFoo = new Foo(273);");
sb.Append("viewModel.fooChoices()[0].showProducts(true);");
In either case it feels a bit off and that a better pattern is out there. Would like to know if one way is better than the other or none of the above.
Many Thanks
Presently, your controller method returns a list of Foo. Consider creating a more complex object that holds both your Foos and your choices.
public class FooViewModel
{
public List<Foo> Foos { get; set; };
public UserChoices { get; set; }
}
Change your controller method so that it returns FooViewModel. This means user choices will be returned along with any Foos you are interested in.
public ActionResult AddFoos()
{
// Are there any choices stored in session?
// Use those first, otherwise create a new UserChoices object
UserChoices choices =
Session["User.Choices"] as UserChoices ?? new UserChoices();
List<Foo> funds = new List<Foo>();
.. etc
FooViewModel vm = new FooViewModel() { Foos = funds; UserChoices = choices };
// Return the whole object, containing Choices and Foos
return Json(vm, JsonRequestBehavior.AllowGet);
}
Also, consider some kind of action filter to allow you to pass complete objects easily. ObjectFilter is a good approach. It allows you to pass complex object structures easily without having to rely on specific markup.
http://www.c-sharpcorner.com/blogs/863/passing-json-into-an-asp-net-mvc-controller.aspx
ObjectFilter above a controller method. Pretty simple, just declaring that the controller should attempt to treat any incoming parameter called fooStuff as type FooViewModel.
[HttpPost,
ObjectFilter(Param = "fooStuff", RootType = typeof(FooViewModel)),
UnitOfWork]
public JsonResult ProcessFoos(FooViewModel fooStuff) {
By defining a corresponding JavaScript view model, you can just convert the whole thing to a json string and pass it to the controller method fully populated.
So, example of corresponding js vm would be:-
var fooViewModel = function(data) {
var self = this;
self.Foos = ko.observableArray(data.Foos);
self.UserChoices = ko.observable(data.UserChoices);
// Don't worry about properties or methods that don't exist
// on the C# end of things. They'll just be ignored.
self.usefulJSOnlyMethod = function() {
// behaviour
};
}
var userChoice = function(data) {
var self = this;
self.DinnerId = ko.observable(data.DinnerId);
}
Typical call to a controller method decorated by ObjectFilter would be something like this ( assuming self is a fooViewModel ):-
var queryData = ko.mapping.toJSON(self);
$.ajax(
//...
data: queryData,
Any matching ( same name, same type case-sensitive ) property from the js vm will automatically end up in the fooStuff parameter of your controller method. Time to save those choices:-
Also note that I'm persisting user choices in the session here. This'll allow them to be picked up by any other controller method which may need them ( example in AddFoos above ).
[HttpPost,
ObjectFilter(Param = "fooStuff", RootType = typeof(FooViewModel)),
UnitOfWork]
public JsonResult ProcessFoos(FooViewModel fooStuff)
{
// hey! I have a fully mapped FooViewModel right here!
// ( _fooServices.ProcessFoos will return updated version of viewmodel )
FooViewModel vm = _fooServices.ProcessFoos(fooStuff);
// What about those choices?
// Put them in the session at this point in case anyone else comes asking
// after them.
Session["User.Choices"] = vm.UserChoices;
return Json(vm);
}
Because we've:-
Defined a better C# view model
Defined a corresponding JS view model
Including UserChoices as part of that view model
....restoring the choice is simple at this point. Reference the part of the view model that contains the user's selected choice.
<select id="dinnerChoice"
data-bind="value: UserChoices.DinnerId"
>
</select>

How to pass class via RedirectToAction

I have the following code:
public ActionResult Index()
{
AdminPreRegUploadModel model = new AdminPreRegUploadModel()
{
SuccessCount = successAddedCount,
FailureCount = failedAddedCount,
AddedFailure = addedFailure,
AddedSuccess = addedSuccess
};
return RedirectToAction("PreRegExceUpload", new { model = model });
}
public ActionResult PreRegExceUpload(AdminPreRegUploadModel model)
{
return View(model);
}
but model is null when I breakpoint on PreRegExcelUpload. Why?
Instead of using the Session object in Evgeny Levin's answer I would suggest to use TempData. See http://rachelappel.com/when-to-use-viewbag-viewdata-or-tempdata-in-asp.net-mvc-3-applications about TempData.
You could also fix this by calling return PreRegExceUpload(model); instead of return RedirectToAction(..) in you Index function.
TempData is just a "smart" wrapper for the Session, under the hood it still acts the same way.
Since it's only 4 fields, i would pass them via querystring.
Always try and avoid session/tempdata where possible, for which in this scenario it certainly is.
Are you sure that's your full code? As it doesn't make sense.
If your POST'ing some data and saving it to the database (for example), usually you redirect to another action passing the unique identifier (which is usually generated after the save), fetch it back from the DB and return the view.
That is much better practice.
If you explain your scenario a bit more, and show the proper code your using, i can help further.
Use session to pass model to method:
public ActionResult Index()
{
AdminPreRegUploadModel model = new AdminPreRegUploadModel()
{
SuccessCount = successAddedCount,
FailureCount = failedAddedCount,
AddedFailure = addedFailure,
AddedSuccess = addedSuccess
};
Session["someKey"] = model;
return RedirectToAction("PreRegExceUpload");
}
public ActionResult PreRegExceUpload()
{
var model = (AdminPreRegUploadModel) Session["someKey"];
Session["someKey"] = null;
return View(model);
}
Method RedirectToAction() can't take non primitive types as parameters, because url parameters is string.

How to have ASP.Net MVC 3.0 Checkboxfor as checked by default?

I want mt view to have the check box checked by default,
I tried something like this.
#Html.CheckBoxFor(model=>model.GenericsOK, new { id = ViewBag.GenericsOK, #checked = true })
and also
#Html.CheckBoxFor(model=>model.GenericsOK, new { id = ViewBag.GenericsOK, #checked = "checked"})
in both cased it give the below error.
String was not recognized as a valid Boolean.
My property is defined as this.
private bool _deafaultchecked = true;
[Display(Name = "Generics Ok")]
public bool GenericsOK
{
get { return _deafaultchecked; }
set { _deafaultchecked = value; }
}
any suggestions please?
Since i could not find a solution or this.
i got this done like this.
#Html.CheckBox("GenericsOK", true, new {id=ViewBag.GenericsOK, name="GenericsOK" })
this works for my requirement.
thanks for all who helped me.
In your controller's Create method (I presume), have you tried this?
public ActionResult Create()
{
return View(new YourModelClass { GenericsOk = true });
}
In the controller action where you create the model just set that field value to true.
For example
return View(new DriverCsvModel{SendEmails = true});
You should be using the state of the model, rather than forcing the UI into a checked state.
You will want to remove the #checked="checked" portion of the HTML attributes. If the viewmodel property is a boolean then it is unnecessary when you use the CheckBoxFor
In the default constructor for your model class, you can set the "GenericsOK" property to "True"

Resources