ASP.NET MVC Model list binding - html

Here is my model:
public class Items
{
public string Foo { get; set; }
public string Bar { get; set; }
}
Controller:
public ActionResult Index()
{
var model = new List<Items>
{
new Items
{
Foo = "foo",
Bar = "bar"
},
new Items
{
Foo = "ai",
Bar = "ia"
},
new Items
{
Foo = "one",
Bar = "two"
}
};
return View(model);
}
[HttpPost]
public ActionResult Index(List<Items> model)
{
return View(model);
}
View (Index):
#using (Html.BeginForm())
{
for (int i = 0; i < Model.Count; i++)
{
<div onclick="$(this).remove();">
#Html.TextBoxFor(model => model[i].Foo) <br/>
#Html.TextBoxFor(model => model[i].Bar)
</div>
}
<div>
<input type="submit"/>
</div>
}
I delete second pair:
<div onclick="$(this).remove();">
<input name="[0].Foo" type="text" value="foo"> <br>
<input name="[0].Bar" type="text" value="bar">
</div>
<div onclick="$(this).remove();">
<input name="[2].Foo" type="text" value="one"> <br>
<input name="[2].Bar" type="text" value="two">
</div>
When posting, i get only first pair ("foo" and "bar"). It's because third pair has index "2". I want to get both pairs(Not using FormCollection. I want it to bind automatically). In reality, I have many other inputs on form, so i don't want to reload and reattach indices to each input. Can you help me?

This may be helpful to you....
need to place Hidden field on each item...
MVC3 Non-Sequential Indices and DefaultModelBinder

I found solution, thanks to Amit Prajapati:
#using (Html.BeginForm())
{
for (int i = 0; i < Model.Count; i++)
{
var identifier = Guid.NewGuid();
<div onclick="$(this).remove();">
#Html.Hidden("Index", identifier)
#Html.TextBox("[" + identifier + "].Foo")
<br/>
#Html.TextBox("[" + identifier + "].Bar")
</div>
}
<div>
<input type="submit" />
</div>
}

Related

PaginatedList onpostasync - ArgumentNullException: Value cannot be null. (Parameter 'model') when submitting on paginnatelist

HI Perhaps someone can point me in the right direction.
I created a paginated list page use the example on the ms site.
https://learn.microsoft.com/en-us/aspnet/core/data/ef-rp/sort-filter-page?view=aspnetcore-5.0
and modified it slightly. I replaced one item with a input box as would like to edit the list and do a bulk save instead on opening every item on a new page to edit it.
but when it click the submit button i get the error.
ArgumentNullException: Value cannot be null. (Parameter 'model')
Microsoft.AspNetCore.Mvc.RazorPages.PageModel.TryValidateModel(object model, string name)
if I bind the property
[BindProperty(SupportsGet = true)]
public PaginatedList<CompanyDataListing> CustomersDisplayList { get; set; }
i get the follow error
ArgumentException: Type 'BizFinder.PaginatedList`1[BizFinder.Data.CompanyDataListing]' does not have a default constructor (Parameter 'type')
and even the paginated list does not render.
My code for the sumbit is as follows.
public PaginatedList<CompanyDataListing> CustomersDisplayList { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!TryValidateModel(CustomersDisplayList, nameof(CustomersDisplayList)))
{
return Page();
}
foreach (var item in CustomersDisplayList)
{
if (item.GoogleCategory != "")
{
string cat = item.GoogleCategory;
}
}
return Page();
}
and my html is as follow.
#using (Html.BeginForm(FormMethod.Post,
new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<table class="table">
<thead>
<tr>
<th>
<a asp-page="./Index" asp-route-sortOrder="#Model.NameSort"
asp-route-currentFilter="#Model.CurrentFilter">
#Html.DisplayNameFor(model => model.CustomersDisplayList[0].CompanyName)
</a>
</th>
<th>
#Html.DisplayNameFor(model => model.CustomersDisplayList[0].Keywords)
</th>
<th>
<a asp-page="./Index" asp-route-sortOrder="#Model.DateSort"
asp-route-currentFilter="#Model.CurrentFilter">
#Html.DisplayNameFor(model => model.CustomersDisplayList[0].GoogleCategory)
</a>
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.CustomersDisplayList)
{
<tr>
<td>
<input type="hidden" asp-for="CustomersDisplayList[0].Id" />
#Html.DisplayFor(modelItem => item.CompanyName)
</td>
<td>
#Html.DisplayFor(modelItem => item.Keywords)
</td>
<td>
#*#Html.DisplayFor(modelItem => item.GoogleCategory)*#
<input asp-for="CustomersDisplayList[0].GoogleCategory" name="Category1" placeholder="Input your keyword" class="form-control GoogleCategory" autofocus="autofocus" />
<span asp-validation-for="CustomersDisplayList[0].GoogleCategory" class="text-danger"></span>
</td>
<td>
<a asp-page="./Edit" asp-route-id="#item.Id">Edit</a> |
<a asp-page="./Details" asp-route-id="#item.Id">Details</a> |
<a asp-page="./Delete" asp-route-id="#item.Id">Delete</a>
</td>
</tr>
}
</tbody>
</table>
<div asp-validation-summary="All">
<span>Please correct the following errors</span>
</div>
#* #Html.ValidationSummary(true, "", new { #class = "text-danger" })*#
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</div>
}
and the full code base is
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using BizFinder.Data;
using BizFinder.ViewModels;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
//https://learn.microsoft.com/en-us/aspnet/core/data/ef-rp/sort-filter-page?view=aspnetcore-5.0
namespace BizFinder.Pages.Admin
{
public class ManCatergoryModel : PageModel
{
private readonly ApplicationDbContext _context;
private readonly IConfiguration Configuration;
private readonly IAuthorizationService _authorizationService;
public int PageSize { get; set; } = 10;
// private readonly SchoolContext _context;
public ManCatergoryModel(ApplicationDbContext context, IConfiguration configuration)
{
_context = context;
Configuration = configuration;
}
public string NameSort { get; set; }
public string DateSort { get; set; }
public string CurrentFilter { get; set; }
public string CurrentSort { get; set; }
// public IList<CompanyDataListing> CustomersDisplayList { get; set; }
// [BindProperty(SupportsGet = true)]
public PaginatedList<CompanyDataListing> CustomersDisplayList { get; set; }
public async Task OnGetAsync(string sortOrder,
string currentFilter, string searchString, int? pageIndex)
{
CurrentSort = sortOrder;
NameSort = String.IsNullOrEmpty(sortOrder) ? "CompanyName" : "";
DateSort = sortOrder == "Keywords" ? "date_desc" : "Date";
if (searchString != null)
{
pageIndex = 1;
}
else
{
searchString = currentFilter;
}
CurrentFilter = searchString;
// using System;
IQueryable<CompanyDataListing> CompData = from s in _context.CompanyDataList
where s.GoogleCategory == null
select s;
switch (sortOrder)
{
case "name_desc":
CompData = CompData.OrderByDescending(s => s.CompanyName);
break;
case "Date":
CompData = CompData.OrderBy(s => s.Keywords);
break;
case "GoogleCategory":
CompData = CompData.OrderByDescending(s => s.GoogleCategory);
break;
default:
CompData = CompData.OrderBy(s => s.CompanyName);
break;
}
// var pageSize = Configuration.GetValue("PageSize", 4);
CustomersDisplayList = await PaginatedList<CompanyDataListing>.CreateAsync(
CompData.AsNoTracking(), pageIndex ?? 1, PageSize);
}
public async Task<IActionResult> OnPostAsync(PaginatedList<CompanyDataListing> Datalist)
{
// List < CompanyDataListing > mm = CustomersDisplayList.ToList();
foreach (var item in Datalist)
{
string str = item.GoogleCategory;
}
return Page();
}
}
}
The first thing you need to know is that the pagination in the tutorial is the back-end pagination. When you click the next page, the data is re-requested every time, so if you want to submit in batches, you can only submit current page data.
If you want the data of the current page to be submitted in batches, then you can modify your code as follows.
PageModel:
public class IndexModel : PageModel
{
private readonly PageListRazorContext _context;
private readonly IConfiguration Configuration;
public IndexModel(PageListRazorContext context, IConfiguration configuration)
{
_context = context;
Configuration = configuration;
}
public string NameSort { get; set; }
public string KeywordsSort { get; set; }
public string CurrentSort { get; set; }
public PaginatedList<CompanyDataListing> CompanyDataListing { get;set; }
public async Task OnGetAsync(string sortOrder,int? pageIndex)
{
NameSort = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
KeywordsSort = sortOrder == "Keywords" ? "keywords_desc" : "Keywords";
IQueryable<CompanyDataListing> company = from s in _context.CompanyDataListing
select s;
switch (sortOrder)
{
case "name_desc":
company = company.OrderByDescending(s => s.CompanyName);
break;
case "Keywords":
company = company.OrderBy(s => s.Keywords);
break;
case "keywords_desc":
company = company.OrderByDescending(s => s.Keywords);
break;
default:
company = company.OrderBy(s => s.CompanyName);
break;
}
var pageSize = Configuration.GetValue("PageSize", 4);
CompanyDataListing = await PaginatedList<CompanyDataListing>.CreateAsync(
company.AsNoTracking(), pageIndex ?? 1, pageSize);
}
public IActionResult OnPost(List<CompanyDataListing> Datalist)
{
//....
return RedirectToPage("./Index");
}
}
Page:
<form method="post">
<table class="table">
<thead>
<tr>
<th>
<a asp-page="./Index" asp-route-sortOrder="#Model.NameSort">
#Html.DisplayNameFor(model => model.CompanyDataListing[0].CompanyName)
</a>
</th>
<th>
<a asp-page="./Index" asp-route-sortOrder="#Model.KeywordsSort">
#Html.DisplayNameFor(model => model.CompanyDataListing[0].Keywords)
</a>
</th>
<th>
#Html.DisplayNameFor(model => model.CompanyDataListing[0].GoogleCategory)
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.CompanyDataListing)
{
<tr>
<td>
<input type="hidden" asp-for="#item.CompanyName" name="Datalist[#i].CompanyName" />
#Html.DisplayFor(modelItem => item.CompanyName)
</td>
<td>
<input type="hidden" asp-for="#item.Keywords" name="Datalist[#i].Keywords" />
#Html.DisplayFor(modelItem => item.Keywords)
</td>
<td>
<input asp-for="#item.GoogleCategory" name="Datalist[#i].GoogleCategory" placeholder="Input your keyword" class="form-control GoogleCategory" autofocus="autofocus" />
<span asp-validation-for="#item.GoogleCategory" class="text-danger"></span>
</td>
<td>
<a asp-page="./Edit" asp-route-id="#item.ID">Edit</a> |
<a asp-page="./Details" asp-route-id="#item.ID">Details</a> |
<a asp-page="./Delete" asp-route-id="#item.ID">Delete</a>
</td>
#{i++;}
</tr>
}
</tbody>
</table>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</div>
#{
var prevDisabled = !Model.CompanyDataListing.HasPreviousPage ? "disabled" : "";
var nextDisabled = !Model.CompanyDataListing.HasNextPage ? "disabled" : "";
}
<a asp-page="./Index"
asp-route-sortOrder="#Model.CurrentSort"
asp-route-pageIndex="#(Model.CompanyDataListing.PageIndex - 1)"
class="btn btn-primary #prevDisabled">
Previous
</a>
<a asp-page="./Index"
asp-route-sortOrder="#Model.CurrentSort"
asp-route-pageIndex="#(Model.CompanyDataListing.PageIndex + 1)"
class="btn btn-primary #nextDisabled">
Next
</a>
</form>
Test result:
You are referencing CustomerDisplayList in the OnPost handler but it has not been initialised, hence the exception. You initialised it in the OnGet handler, but that only executes for GET requests. If you want to use a POST request, you also need to initialise the CustomerDisplayList in that too.
For what it's worth, the example you linked to uses GET requests for the whole cycle. This usually makes more sense for things like paginating and filtering results. That way, the criteria are passed in the URL as query string values or route data, which means that you can bookmark filtered/paged results. You won't need to render the anti forgery token for GET requests.

MVC Page Model's List Items on Submit to use in Post Method

I have a list that is created and filled with values upon the page loading. When the page is submitted, I need to use those values from the list in the post method.
Is there a way to do that for list items?
For other fields I do this:
< input asp-for="FileID" type="hidden" />
But I have this list in the Model:
public List<String> DocumentListing { get; set; }
But both of the attempts below do not allow me to see the list items in Controller's Post method on submit
int DocumentsCount = Model.DocumentListing.Count();
for (int i = 0; i < DocumentsCount; i++)
{
Model.DocumentListing.Add(Model.DocumentListing.ElementAtOrDefault(i));
<input asp-for="DocumentListing.ElementAtOrDefault(i)" type="hidden">
}
Can someone please help?
According to the doc,
If you want to accept the list, the request data should be like DocumentListing[0],DocumentListing[1]....
An example below:
<form method="post">
<input asp-for="FileID" type="hidden" />
#for (int i = 0; i < Model.DocumentListing.Count(); i++)
{
<input asp-for="DocumentListing[i]" type="text">
}
<input type="submit" value="submit" class="btn btn-danger" />
</form>
Controller:
public IActionResult Index()
{
var test = new Test
{
FileID = 1,
DocumentListing = new List<string> { "A", "B", "C" }
};
return View(test);
}
[HttpPost]
public IActionResult Index(Test test)
{
return View(test);
}
Result:

Blazor/Razor: InputSelect with Enum?

How can I use the values of an enum class as options for an InputSelect?
Example enum:
public enum Test
{
Test1,
Test2
}
I am using with Blazor with Razor components.
Here's a working sample how to use enum in InputSelect component:
<EditForm EditContext="#EditContext">
<DataAnnotationsValidator />
<div class="form-group">
<label for="name">Enter your Name: </label>
<InputText Id="name" Class="form-control" #bind-Value="#comment.Name"></InputText>
<ValidationMessage For="#(() => comment.Name)" />
</div>
<div class="form-group">
<label for="body">Select your country: </label>
<InputSelect #bind-Value="#comment.Country" >
#foreach (var country in Enum.GetValues(typeof(Country)))
{
<option value="#country">#country</option>
}
</InputSelect>
<ValidationMessage For="#(() => comment.Country)" />
</div>
<p>
<button type="submit">Submit</button>
</p>
</EditForm>
#code
{
private EditContext EditContext;
private Comment comment = new Comment();
protected override void OnInitialized()
{
EditContext = new EditContext(comment);
base.OnInitialized();
}
public enum Country
{
USA = 1,
Britain,
Germany,
Israel
}
public class Comment
{
public string Name { get; set; }
public Country Country { get; set; }
}
}
Hope this helps...
So in short it's like this:
<InputSelect #bind-Value="#YourEnum">
#foreach (var value in Enum.GetValues<YourEnumType>()) {
<option value="#value">#value</option>
}
</InputSelect>
#enet answer is correct.
To add to this, sometimes enum values won't accept specific chars. you can edit the display of enum value to display specific chars in the code if you have filtered or changed enum values to store correctly. like below in my enum i replace all spaces with underscore. so you can add this to the code to display the enum values on the dropdown correctly.
<InputSelect #bind-Value="#updateUserDetails.Nationality" class="dropDownSelectList">
#foreach (var countryName in Enum.GetValues(typeof(Nationality)))
{
<option value="#countryName">#(#countryName.ToString().Replace("_", " ").Replace("HH", "-"))</option>
}
</InputSelect>
<select>
#foreach (var Item in Enum.GetValues(typeof( DayOfWeek)))
{
<option value="#Item">#Item</option>
}

Edit view not being populated with objects data

I am quite new to ASP .Net, and could use some help... I have an ASP .Net Core 1.1 web app. In it, I have an "Edit" view for editing a simple object, which a corresponding controller calls when routed to it. This is the view:
#model InspectionsTestClient.Models.Property
#{
ViewData["Title"] = "Edit";
}
<h2>Edit</h2>
#Html.ValidationSummary();
<form asp-action="Edit">
<div class="form-horizontal">
<h4>Property</h4>
<hr />
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="Id" />
<div class="form-group">
<label asp-for="UnitNumber" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="UnitNumber" class="form-control" />
<span asp-validation-for="UnitNumber" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<label asp-for="BuildingName" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="BuildingName" class="form-control" />
<span asp-validation-for="BuildingName" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<label asp-for="Street" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Street" class="form-control" />
<span asp-validation-for="Street" class="text-danger"></span>
</div>
</div>
</div>
</form>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
This is the controller which calls that view:
// GET: Property/Edit/5
public ActionResult Edit(int id)
{
return View();
}
And this is the model:
namespace InspectionsTestClient.Models
{
//[Table("property")]
public class Property
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int? Id { get; set; }
[MaxLength(10, ErrorMessage = "Unit number too long")]
[Display(Name = "Unit #")]
public string UnitNumber { get; set; }
[MaxLength(45, ErrorMessage = "BuildingName name too long")]
public string BuildingName { get; set; }
[MaxLength(45, ErrorMessage = "Street too long")]
public string Street { get; set; }
}
}
So when I navigate to that page, the controller fires up, and returns the Edit view. I have confirmed the parameter "id" is populated. When the Edit view loads in the browser, however, all the input textboxes are empty. I would expect them to be pre-populated with the values for the object in question. What am I missing?
The issue you are experiencing is happening because you are not returning that object to the view.. actually in your case you're not even going out to the db to get the object.
You need to edit you Edit action to something like this:
// GET: Property/Edit/5
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var object = db.TableName.Find(id);
// db = connectionstring
// TableName = database table that holds the object that you want to return
if (object == null)
{
return HttpNotFound();
}
return View(object);
}
Let me know if this helps
public class PropertyController
{
private readonly ApplicationDbContext _dbContext;
public PropertyController(ApplicationDbContext dbContext){
_dbContext = dbContext;
}
//GET: Property/Edit/5
public async Task<IActionResult> Edit(int id)
{
var property = await _dbContext.Property.FirstOrDefaultAsync(p => p.Id == id);
return View(property);
}
}
If you don't pull the data from the database and send it to the view of course it will always be blank. Edit(int id) there will be 2, both slightly different from the other.
[HttpPost]
[ValidateAntiForgeryToken]
//Post: Property/Edit/5
public async Task<IActionResult> Edit(int id, [Bind("Id", "UnitNumber", "BuildingNumber", "Street")] Property property)
{
if(ModelState.IsValid){
}
else{
}
}
not everyting is present but that is part of your adventure.

How to check if item is selected in a dropdownList and enable/disable a textbox if a listitem is selected/not selected

I have a page where i have created a form of sorts.
I have 2 dropdownlists(profilelist & salarylist).
I have 3 textboxes in this form. What i want to do:
I have 2 boxes where i create a new profile and a new salarygroup and the new profile is added to the profillist amd the salarygroup is added to the salarylist.
Now I want the third box to be disabled UNTIL an item in both the salarylis and profillist is selected. once items are selected the textbox should be enabled.
My view:
#model KUMA.Models.EmployeeCardAdminModel
#{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<div class="row bgwhite">
<div class="twelve columns">
<h2>Administration KUMA</h2>
<div class="row bgwhite">#using (Html.BeginForm("Index", "Admin"))
{
<div class="three columns">
#Html.DropDownList("UserId", (SelectList)ViewBag.UserId, "--Välj anställd--")
</div>
<div class="three columns" style="margin-right:457px !important;">
#Html.DropDownList("Salaryid", (SelectList)ViewBag.Salaryid, "--Välj LöneGrupp--")
<input style="float:left;" type="submit" value="Knyt" />
</div>
}
#using (Html.BeginForm("Kompetens", "KumaAdmin"))
{
<div class="three columns" style="margin-right: 627px;">
<h6>Kompetenser</h6>
<div style="width:456px;"> #Html.ListBox("kompetensId", (SelectList)ViewBag.KomId, new { placeholder = "Kompetenser" })</div><br/>
#Html.TextBoxFor(mm => mm.Kompetens, new { placeholder = "Ange Kompetens" })
#*#Html.TextBoxFor(mm => mm.KompetensTest, new { placeholder = "Kompetens" })
#Html.TextAreaFor(mm => mm.KompetensTest, new { placeholder = "Kompetens", rows="5", cols="80" })*#
<input type="submit" style="margin-right: 205px;" value="Skapa"/><br/><br/>
</div>
}
#using (Html.BeginForm("Index", "KumaAdmin"))
{
<div class="three columns" style="margin-right: 627px;">
#* <input name="profileTxtBox"type="text" style="width:97px; height:28px;" value="" />*#
#Html.TextBoxFor(mm => mm.Profile, new { placeholder = "Ange Profil" })
#Html.TextBoxFor(mm => mm.SalaryGroup, new { placeholder = "Ange LöneGrupp" })
<input type="submit" value="Skapa"/>
</div>
}
#* <div class="one columns" style=" margin-left: 100px;margin-right: 164px; margin-top: -33px;">
<input name="profileTxtBox"type="submit" style="width:100px;" value="Add" />
</div>*#
<div class="five columns"></div>
</div>
</div
>
Controller:
public class AAdminController : Controller
{
static List<Employee> list = new List<Employee>();
//EmployeeCardAdminModel employee = new EmployeeCardAdminModel();
//
// GET: /Admin/
//[Authorize(Roles = "Admin")]
[HttpGet]
public ActionResult Index()
{
ViewBag.UserId = new SelectList(list, "Id", "Profile");
ViewBag.Salaryid = new SelectList(list, "Id", "SalaryGroup");
ViewBag.KomId = new SelectList(list, "Id", "Kompetens");
ModelState.Clear();
return View("Index");
}
[HttpPost]
// submit for profile & salary box
public ActionResult Index(Models.EmployeeCardAdminModel e)
{
if (string.IsNullOrEmpty(e.Profile) == false && string.IsNullOrEmpty(e.SalaryGroup) == false)
{
// adda a new employye to the list and set the values from the parameter to the model
list.Add(new Employee
{
Id = e.Id + 1,
Profile = e.Profile,
SalaryGroup = e.SalaryGroup
});
}
return (Index());
}
[HttpPost]
// submit for knowledge box
public ActionResult Kompetens(Models.EmployeeCardAdminModel e)
{
if (string.IsNullOrEmpty(e.Kompetens) == false)
{
// adda a new employye to the list and set the values from the parameter to the model
list.Add(new Employee
{
Kompetens = e.Kompetens
});
}
return (Index());
}
And finally my model(note that the emplyee class is the same as my model, with the same properties but ive read that for best practice it is best to separate these.):
public class EmployeeCardAdminModel
{
public string Profile { get; set; }
public int Id { get; set; }
public string SalaryGroup { get; set; }
public string Kompetens { get; set; }
}
the way i would normaly do this is to call the desired lists and check if the selected index is larger than null but the problem is i don't know how to access the list from the controller in the correct way so i can get access to the items in it. also how can i get an id on the textboxes? I need this to be able to disable/enable the correct textbox.
Im pretty new to mvc and i learn by doing projects so all advice is appreciated.
Thank you!
Well after some time I realized that there wasn't a good way of doing this without script.
So i solved it with jquery like this:
$(function () {
$(".list select").change(function () {
if ($(".list1 select").val().length == 0 || $(".list2 select").val().length == 0) {
$(".kompetensbox input").attr('disabled', 'disabled');
}
else {
$(".kompetensbox input").removeAttr('disabled');
}
})
});
view changes (added css classes):
<div class="three columns list list1">
#Html.DropDownList("UserId", (SelectList)ViewBag.UserId, "--Välj profilgrupp--")
</div>
<div class="three columns list list2" style="margin-right:457px !important;">
#Html.DropDownList("Salaryid", (SelectList)ViewBag.Salaryid, "--Välj LöneGrupp--")
<input style="float:left;" type="submit" value="Knyt" />
</div>
Hope it helps others that attempt the same.