MVC is it possible to target blank + parameters - html

See this :
#Html.ActionLink("link", "Func", new { controller = "MyControl" }, new { target = "_blank" })
It does what it is suposed to do, but what if i need my model, because here is the header of my function :
public ActionResult Func(model_1 m)
{ }
What i'm trying to do is open a new tab, and carry my model to this new tab... how can i do?

There is an overload of Html.ActionLink helper method which allows you to pass route values.
#Html.ActionLink("link", "Func", "MyControl" ,new { EmpId=1, EmpCode="E23" ,Age =23},
new { target = "_blank" })
This will basically generate an anchor tag with href value with querystring formed from the route values you provided.
link
Assuming you have a class with these 2 properties being used as the parameter of the action method
public class EmployeeVm
{
public int EmpId { set;get;}
public string EmpCode { set;get;}
public int Age{ set;get;}
}
and this is being used as the type of your action method argument
public ActoinResult Func(EmployeeVm model)
{
// To do : Return something
}
The model binder will be able to map the querystring values to the properties of the parameter object.
But remember, querystring's has limitations in how much data it can carry. Also the above approach work for a lean-flat view model class. It won't work for a complex viewmodel class where your properties are other classses /collection of other types.
In that case, The best solution is to pass a unique id / combination of Ids and use that to rebuild your model / view model in the second action method.
#Html.ActionLink("link", "Func", "MyControl" ,new { EmpId=1}, new { target = "_blank" })
and in your action method
public ActionResult Func(int empId)
{
// to do : Using empIdvalue, Get the View model /Model data
// Ex : EmployeeVm emp = SomeService.GetEmployeeFromId(empId)
}
Assuming SomeService.GetEmployeeFromId accepts an employeeId and return an object of EmployeeVm. The method can query your db table to get the corresponding employee record for the id passed in and build the EmployeeVm object from that data.

Related

Unable to get SelectList working in ASP.NET MVC

I am trying to get my SelectList working. I tried adding the options in the model class, but I'm unable to get that working with an error
Member Testing.TaxIDType cannot be accessed with an instance reference; qualify it with a type name instead
which I am not sure about how to fix.
I added the SelectList items to my model:
public static List<SelectListItem> TaxIDType = new List<SelectListItem>()
{
new SelectListItem() { Text = "FEIN", Value = "FEIN" },
new SelectListItem() { Text = "SSN", Value = "SSN" }
};
I try to call the in the view using the following statement.
#Html.DropDownListFor(Model => Model.TaxIDType, class.TaxIDType)
I am getting the following two errors:
'IEnumerable' does not contain a definition for 'TaxIDType' and no accessible extension method 'TaxIDType' accepting a first argument of type 'IEnumerable' could be found (are you missing a using directive or an assembly reference?)
Member 'Testing.TaxIDType' cannot be accessed with an instance reference; qualify it with a type name instead
Can someone help me with this?
Here is a step by step explanation of using DropDownListFor helper in your case.
Create a model which will contain a collection of SelectListItem instances.
public class MyModel
{
public List<SelectListItem> TaxIDType = new List<SelectListItem>()
{
new SelectListItem() { Text = "FEIN", Value = "FEIN" },
new SelectListItem() { Text = "SSN", Value = "SSN" }
};
}
Note: if you want, you can even make the property static, however, you will need to change the way you reference the property on the view (will explain it eventually)
Create the model and pass it to the view.
public ActionResult Index()
{
var viewModel = new MyModel();
return View(viewModel);
}
In the view, state that your view uses MyModel class. You can do it by putting the #model helper on the top of your view (.cshtml file).
#model WebApplication3.Controllers.MyModel
Here, WebApplication3.Controllers is a namespace of your model and MyModel is it's name. Don't use the listed namespace and model, but change them to your own.
Use the DropDownListFor helper as following
#Html.DropDownListFor(x => ??, Model.TaxIDType)
In the example, Model is a property which is being populated by the framework to let your acecss a class instance which you passes in View() method inside the controller. (MyModel instance in this case).
By writing Model.TaxIDType you are referencing the property which contains the list of items to poppulate the dropdown.
Notice, that the property in the model isn't marked as static, that's why we have to access it via the instance (Model) reference. However, you can, indeed, make the property static. In that case, you will need to change the usage in the view to the following:
#Html.DropDownListFor(x => ??, MyModel.TaxIDType)
The last thing to mention: take a look at the question marks. In order to get the selected value back, you will need to map it to the model. One way to do it is to extend the model.
public class MyModel
{
public string SelectedTaxIdType { get; set; }
public List<SelectListItem> TaxIDType = new List<SelectListItem>()
{
new SelectListItem() { Text = "FEIN", Value = "FEIN" },
new SelectListItem() { Text = "SSN", Value = "SSN" }
};
}
and than modify the view
#Html.DropDownListFor(x => x.SelectedTaxIdType, Model.TaxIDType)

Dependecy between two different json files in restassured

I have Created two java classes TestA.java,TestB.java using restAssured where each of the class reads json from TestA.json and testB.json and post a request to endpoint uri.TestA.java returns a json response having tag "customerID" which will be input for one of the tags of TestB.json and when ever I post a request using "TestB.java" customerID has to be picked from TestB.json .How do my code look like?Any ideas?
My code :
TestA.java
String requestBody = generateString("CreateCustomer.json");
RestAssured.baseURI = "https://localhost:8080";
Response res = given().contentType(ContentType.JSON)
.header("Content-Type", "application/json").header("checkXML", "N").body(requestBody).when()
.post("/restservices/customerHierarchy/customers").then().assertThat()
.statusCode(201).and().body("transactionDetails.statusMessage", equalTo("Success")).and().log().all()
.extract().response();
//converts response to string
String respose = res.asString();
JsonPath jsonRes = new JsonPath(respose);
CustomerID = jsonRes.getString("customerNodeDetails.customerId");
TestA.java response
{
"customerNodeDetails": {
"customerId": "81263"
},
Now i want to pass this customerID as input in testB.json or testB.java which is dynamic.
TestB.json
"hierarchyNodeDetails": {
"**customerId**":"",
"accountNumber": "",
"startDate": "",
}
Both TestA.java and TestB.java looks almost same except the post uri
Thanks in Advance
It depends on how you are distributing your classes:
If you want to write the tests for A and B in a single class. Declare a local variable of type Response/String and then store the customer ID in that variable. The scope of the variable will be live in all TestNG methods. You can set the customer ID for the B.json from the local variable.
public class Test{
String _cust_id;
#Test
public void test_A(){
// ceremony for getting customer id
_cust_id = jsonRes.getString("customerNodeDetails.customerId");
}
#Test
public void test_B(){
// write value of customer id using the variable _cust_id
}}
You can try this approach, but would suggest separating the data part to a dataProvider class.
If you want to have separate classes for A and B, use ITestContext to pass values from one class to the other.
public class A{
#Test
public void test1(ITestContext context){
context.setAttribute("key", "value");
}
}
public class B{
#Test
public void test2(ITestContext context){
String _attribute = context.getAttribute(key);
}
}
The elegant way could be, use a dataProvider for class B test where you perform the ceremony of getting the customerID from class A Tests.
public class DataB{
#DataProvider
public static Object[][] _test_data_for_b(){
// get the customerID
// create the json for class B
// return the json required for class B test
// All these could be achieved as everything has a common scope
}}
public class B{
#Test(dataProvider = "_test_data_for_b", dataProviderClass = DataB.class)
public void test_B(String _obj){
// perform the testing
}}

ASP .NET MVC Drop Down List throwing multiple errors when empty [duplicate]

I have the following view model
public class ProjectVM
{
....
[Display(Name = "Category")]
[Required(ErrorMessage = "Please select a category")]
public int CategoryID { get; set; }
public IEnumerable<SelectListItem> CategoryList { get; set; }
....
}
and the following controller method to create a new Project and assign a Category
public ActionResult Create()
{
ProjectVM model = new ProjectVM
{
CategoryList = new SelectList(db.Categories, "ID", "Name")
}
return View(model);
}
public ActionResult Create(ProjectVM model)
{
if (!ModelState.IsValid)
{
return View(model);
}
// Save and redirect
}
and in the view
#model ProjectVM
....
#using (Html.BeginForm())
{
....
#Html.LabelFor(m => m.CategoryID)
#Html.DropDownListFor(m => m.CategoryID, Model.CategoryList, "-Please select-")
#Html.ValidationMessageFor(m => m.CategoryID)
....
<input type="submit" value="Create" />
}
The view displays correctly but when submitting the form, I get the following error message
InvalidOperationException: The ViewData item that has the key 'CategoryID' is of type 'System.Int32' but must be of type 'IEnumerable<SelectListItem>'.
The same error occurs using the #Html.DropDownList() method, and if I pass the SelectList using a ViewBag or ViewData.
The error means that the value of CategoryList is null (and as a result the DropDownListFor() method expects that the first parameter is of type IEnumerable<SelectListItem>).
You are not generating an input for each property of each SelectListItem in CategoryList (and nor should you) so no values for the SelectList are posted to the controller method, and therefore the value of model.CategoryList in the POST method is null. If you return the view, you must first reassign the value of CategoryList, just as you did in the GET method.
public ActionResult Create(ProjectVM model)
{
if (!ModelState.IsValid)
{
model.CategoryList = new SelectList(db.Categories, "ID", "Name"); // add this
return View(model);
}
// Save and redirect
}
To explain the inner workings (the source code can be seen here)
Each overload of DropDownList() and DropDownListFor() eventually calls the following method
private static MvcHtmlString SelectInternal(this HtmlHelper htmlHelper, ModelMetadata metadata,
string optionLabel, string name, IEnumerable<SelectListItem> selectList, bool allowMultiple,
IDictionary<string, object> htmlAttributes)
which checks if the selectList (the second parameter of #Html.DropDownListFor()) is null
// If we got a null selectList, try to use ViewData to get the list of items.
if (selectList == null)
{
selectList = htmlHelper.GetSelectData(name);
usedViewData = true;
}
which in turn calls
private static IEnumerable<SelectListItem> GetSelectData(this HtmlHelper htmlHelper, string name)
which evaluates the the first parameter of #Html.DropDownListFor() (in this case CategoryID)
....
o = htmlHelper.ViewData.Eval(name);
....
IEnumerable<SelectListItem> selectList = o as IEnumerable<SelectListItem>;
if (selectList == null)
{
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,
MvcResources.HtmlHelper_WrongSelectDataType,
name, o.GetType().FullName, "IEnumerable<SelectListItem>"));
}
Because property CategoryID is typeof int, it cannot be cast to IEnumerable<SelectListItem> and the exception is thrown (which is defined in the MvcResources.resx file as)
<data name="HtmlHelper_WrongSelectDataType" xml:space="preserve">
<value>The ViewData item that has the key '{0}' is of type '{1}' but must be of type '{2}'.</value>
</data>
according to stephens (user3559349) answer, this can be useful:
#Html.DropDownListFor(m => m.CategoryID, Model.CategoryList ?? new List<SelectListItem>(), "-Please select-")
or in ProjectVM:
public class ProjectVM
{
public ProjectVM()
{
CategoryList = new List<SelectListItem>();
}
...
}
Most Likely Caused some sort of error redirecting to your page and you not initializing your model's drop down lists again.
Make sure that you initialize your drop downs in either the model's constructor or every time before you send said model to the page.
Otherwise you will need to maintain the state of the drop down lists either through the view bag or through the hidden value helpers.
OK, the poster's canned answer neatly explained why the error occurred, but not how to get it to work. I'm not sure that's really an answer, but it did point me in the right direction.
I ran into the same issue and found a slick way to resolve it. I'll try to capture that here. Disclaimer - I work on web pages once a year or so and really don't know what I'm doing most of the time. This answer should in no way be considered an "expert" answer, but it does the job with little work...
Given that I have some data object (most likely a Data Transfer Object) that I want to use a drop-down list to supply valid values for a field, like so:
public class MyDataObject
{
public int id;
public string StrValue;
}
Then the ViewModel looks like this:
public class MyDataObjectVM
{
public int id;
public string StrValue;
public List<SectListItem> strValues;
}
The real problem here, as #Stephen so eloquently described above, is the select list isn't populated on the POST method in the controller. So your controller methods would look like this:
// GET
public ActionResult Create()
{
var dataObjectVM = GetNewMyDataObjectVM();
return View(dataObjectVM); // I use T4MVC, don't you?
}
private MyDataObjectVM GetNewMyDataObjectVM(MyDataObjectVM model = null)
{
return new MyDataObjectVM
{
int id = model?.Id ?? 0,
string StrValue = model?.StrValue ?? "",
var strValues = new List<SelectListItem>
{
new SelectListItem {Text = "Select", Value = ""},
new SelectListITem {Text = "Item1", Value = "Item1"},
new SelectListItem {Text = "Item2", Value = "Item2"}
};
};
}
// POST
public ActionResult Create(FormCollection formValues)
{
var dataObject = new MyDataObject();
try
{
UpdateModel(dataObject, formValues);
AddObjectToObjectStore(dataObject);
return RedirectToAction(Actions.Index);
}
catch (Exception ex)
{
// fill in the drop-down list for the view model
var dataObjectVM = GetNewMyDataObjectVM();
ModelState.AddModelError("", ex.Message);
return View(dataObjectVM);
)
}
There you have it. This is NOT working code, I copy/pasted and edited to make it simple, but you get the idea. If the data members in both the original data model and the derived view model have the same name, UpdateModel() does an awesome job of filling in just the right data for you from the FormCollection values.
I'm posting this here so I can find the answer when I inevitably run into this issue again -- hopefully it will help someone else out as well.
I had the same problem, I was getting an invalid ModelState when I tried to post the form. For me, this was caused by setting CategoryId to int, when I changed it to string the ModelState was valid and the Create method worked as expected.
In my case the first ID in my list was zero, once I changed the ID to start from 1, it worked.

AutoMapper - passing parameter to custom resolver weird behavior

Although I'm relatively new to AutoMapper I'm using it in a small project I'm developing. I've never had problems using it before but now I'm facing some weird behavior passing parameters to a Custom Resolver.
Here's the scenario: I get a list of messages from my repository and then map those to a frontend friendly version of it. Nothing fancy, just some normal mapping between objects. I have a field in that frontend object that tells if a certain user already voted for that message and that's what I'm using the Custom Resolver for (it's that second "ForMember"):
public List<SupportMessageUi> GetAllVisible(string userId)
{
Mapper.CreateMap<SupportMessage, SupportMessageUi>()
.ForMember(dest => dest.Votes,
opt => opt.ResolveUsing<SupportMessageVotesResolver>())
.ForMember(dest => dest.UserVoted,
opt => opt.ResolveUsing<SupportMessagesUserVotedResolver>()
.ConstructedBy(() => new SupportMessagesUserVotedResolver(userId)));
var messages = _unitOfWork.MessagesRepository.Get(m => m.Visible);
var messagesUi = Mapper.Map<List<SupportMessageUi>>(messages);
return messagesUi;
}
I'm calling this method on a web service and the problem is: the first time I call the webservice (using the webservice console) it all runs perfectly. For example, if I pass '555' as the userId I get to this method with the correct value:
And in the Custom Resolver the value was correctly passed to the constructor:
The results returned are correct. The problem comes next. The second time I call the service, passing a different argument ('666' this time) the argument that gets to the constructor of the Custom Resolver is the old one ('555'). Here's what I mean:
Right before mapping the objects we can see that the value passed to the constructor was correct ('666'):
But when it gets to the constructor of the Resolver the value is wrong, and is the old one ('555'):
All subsequent calls to the service use the original value in the Custom Resolver constructor ('555'), independently of the value I pass to the service (also happens if I make the call from another browser). If I shut down the server and relaunch it I can pass a new parameter (that will be used in all other calls until I shut it down again).
Any idea on why this is happening?
It's happening because AutoMapper.CreateMap is a static method, and only needs to be called once. With the CreateMap code in your web method, you're trying to call it every time you call that method on your web service. Since the web server process stays alive between calls (unless you restart it, like you said) then the static mappings stay in place. Hence, the necessity of calling AutoMapper.Reset, as you said in your answer.
But it's recommended that you put your mapping creation in AppStart or Global or a static constructor or whatever, so you only call it once. There are ways to call Map that allow you to pass in values, so you don't need to try to finesse things with the constructor of your ValueResolver.
Here's an example using a ValueResolver (note the change to implementing IValueResolver instead of inheriting ValueResolver<TSource, TDestination>):
[Test]
public void ValueTranslator_ExtraMapParameters()
{
const int multiplier = 2;
ValueTranslator translator = new ValueTranslator();
Mapper.AssertConfigurationIsValid();
ValueSource source = new ValueSource { Value = 4 };
ValueDest dest = translator.Translate(source, multiplier);
Assert.That(dest.Value, Is.EqualTo(8));
source = new ValueSource { Value = 5 };
dest = translator.Translate(source, multiplier);
Assert.That(dest.Value, Is.EqualTo(10));
}
private class ValueTranslator
{
static ValueTranslator()
{
Mapper.CreateMap<ValueSource, ValueDest>()
.ForMember(dest => dest.Value, opt => opt.ResolveUsing<ValueResolver>().FromMember(src => src.Value));
}
public ValueDest Translate(ValueSource source, int multiplier)
{
return Mapper.Map<ValueDest>(source, opt => opt.Items.Add("multiplier", multiplier));
}
private class ValueResolver : IValueResolver
{
public ResolutionResult Resolve(ResolutionResult source)
{
return source.New((int)source.Value * (int)source.Context.Options.Items["multiplier"]);
}
}
}
private class ValueSource { public int Value { get; set; } }
private class ValueDest { public int Value { get; set; } }
And here's an example using a TypeConverter:
[Test]
public void TypeTranslator_ExtraMapParameters()
{
const int multiplier = 3;
TypeTranslator translator = new TypeTranslator();
Mapper.AssertConfigurationIsValid();
TypeSource source = new TypeSource { Value = 10 };
TypeDest dest = translator.Translate(source, multiplier);
Assert.That(dest.Value, Is.EqualTo(30));
source = new TypeSource { Value = 15 };
dest = translator.Translate(source, multiplier);
Assert.That(dest.Value, Is.EqualTo(45));
}
private class TypeTranslator
{
static TypeTranslator()
{
Mapper.CreateMap<TypeSource, TypeDest>()
.ConvertUsing<TypeConverter>();
}
public TypeDest Translate(TypeSource source, int multiplier)
{
return Mapper.Map<TypeDest>(source, opt => opt.Items.Add("multiplier", multiplier));
}
private class TypeConverter : ITypeConverter<TypeSource, TypeDest>
{
public TypeDest Convert(ResolutionContext context)
{
TypeSource source = (TypeSource)context.SourceValue;
int multiplier = (int)context.Options.Items["multiplier"];
return new TypeDest { Value = source.Value * multiplier };
}
}
}
private class TypeSource { public int Value { get; set; } }
private class TypeDest { public int Value { get; set; } }
Answering myself: I was not using AutoMapper.Reset(). Once I did that everything started working properly.
Helpful reading: http://www.markhneedham.com/blog/2010/01/27/automapper-dont-forget-mapper-reset-at-the-start/

LinqToSql Calculated Field OnPropertyIDChanged

I have a partial class to extend one of my LinqToSql classes. In this partial class I have the following calculated field.
public bool IsInCluster
{
get
{
return Cluster != null;
}
}
In order for a grid column databound to this field to update automatically I have implemented the following partial method.
partial void OnClusterIDChanged()
{
SendPropertyChanged("IsInCluster");
}
However when I update the Cluster property as shown in the following code the OnClusterIDChanged method does not get called:
private void ExecCreateClusterCommand()
{
var cluster = new Cluster()
{
ID = Guid.NewGuid(),
MailService = AppState.CurrentMailService
};
App.DataContext.Clusters.InsertOnSubmit(cluster);
foreach (DeliveryPoint deliveryPoint in SelectedDeliveryPoints)
{
deliveryPoint.Cluster = cluster;
}
App.DataContext.SubmitChanges();
}
I have successfully used this technique with other non navigation properties related to calculated fields. Is there a way to make this work?
In your setter for Cluster, call OnClusterIDChanged, if the state has changed.
The only solution I could find for this was to create a public method in the DeliveryPoint class enabling me to call SendPropertyChanged for the required field (navigation property):
public void CallSendPropertyChanged(string propertyName)
{
SendPropertyChanged(propertyName);
}