ASP.NET MVC2 -- Trouble with Model Binding and Html.Textbox() - asp.net-mvc

ASP.NET MVC Model Binding is still new to me and I'm trying to understand exactly how it works. Right now, I appear to be having problems with a feature of Html.Textbox()
Specifically, I have a View where I set Html.Textbox to a value both in the "Get" and the "Post". It sets fine in the "Get", but after the user submits a value during the "Post", I have the class change one of the values internally based on the other value submitted.
(I'm basically validating one value based on the other... I'm not sure if this is the right way to do this...)
Tracing through, I can see that the value has actually changed as expected both in the Model and in the View, but when it displays on my screen after the "Post", the value does not display as it was changed. Instead it is what it was set to originally.
Here's my simplified example:
The View shows a:
Drop-down with items from a SelectList (pre-selected as "Other")
a Read-only Text Box (with a pre-loaded value of 0)
Submit Button
User should pick a new value from the Drop-Down and click submit. The "Post" method in the controller picks up the new value from the Drop-Down and changes the Value in the Read-only text-box and re-displays.
(Yes, I'll eventually be doing this with JQuery, too...)
Here's my sample Model class:
public class SampleSubmission
{
public string Name { get; set; }
public int Volume { get; set; }
public readonly SortedList<string, int> NameVolumeList = new SortedList<string, int>();
// Standard Constructor
public SampleSubmission()
{
NameVolumeList.Add("Sample1", 10);
NameVolumeList.Add("Sample2", 20);
NameVolumeList.Add("Sample3", 50);
NameVolumeList.Add("Other", 0);
this.Name = NameVolumeList.Keys[0];
this.Volume = NameVolumeList[Name];
}
// Copy Constructor
public SampleSubmission(SampleSubmission samSub) : this()
{
this.Name = samSub.Name;
this.Volume = NameVolumeList[Name];
}
}
Here's the Controller:
public class SampleSubmissionController : Controller
{
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Index()
{
SampleSubmission sampleSub = new SampleSubmission();
return View(sampleSub);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(SampleSubmission sampleSub)
{
SampleSubmission samSub = new SampleSubmission(sampleSub);
return View(samSub);
}
}
Here's the View:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<MvcModelBindTest.Models.SampleSubmission>" %>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<% using (Html.BeginForm()) { %>
<%= Html.DropDownList("Name", new SelectList(Model.NameVolumeList.Keys.ToList())) %>
<%= Html.TextBox("Volume",Model.Volume) %>
<input type="submit" name="pick" id="pick" value="Pick" /> <% } %>
</asp:Content>
Any ideas as to why the new value does not display?
EDIT:
In order to fix the problem, I read the link given by "jfar" and made a 1-line change.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(SampleSubmission sampleSub)
{
SampleSubmission samSub = new SampleSubmission(sampleSub);
// Reset Model Value
ModelState.SetModelValue("Volume", new ValueProviderResult(
samSub.Volume, "", System.Globalization.CultureInfo.CurrentCulture));
return View(samSub);
}
This definitely works. Unfortunately, this feels like a gross hack to me. What if I had to update the values of multiple fields? There must be a better (simpler?) way of doing this.
EDIT2: Found my answer. See below...

From: How to clear textboxes defined with MVC HTML helpers
"The HTMLHelper's first look at the
ModelState and ViewData to see if any
values match their key and then
finally use whatever value you provide
them.
If you need to reset the textboxe's
value you also need to clear the
ModelState entry with the matching
key. Another alternative is
redirecting to the same page instead
of simply rendering a view via
javascript or with MVC.

I figured out the answer to my own question when I stumbled upon another variable that needed to be reset. As I was looking at the data structure, I realized what I wanted was the pristine state where there were no Keys in the ModelState.
ModelState.Remove(key);
Where "key" is the value you're trying to reset.

Another simple workaround is instead of
<%= Html.TextBox("Volume",Model.Volume) %>
Use HTML input tag
<input id="Volume" name ="Volume" value="#Model.Volume" />

Related

How to pass multiple Html.DropDownList selected values from View( .aspx ) to MVC controller's action?

I need to pass multiple data ( probably 2 Html.DropDownList's selected values ) to MVC controller action method from MVC View ( .aspx). I think it would be from somehow Html.Hidden form , but how?
I am unable to get the selected value from Html.DropDownList and pass it as Html.Hidden("paramName", MvcStringSelectedValue) to controller's action.
My Code is :
based on<br />
<%: Html.DropDownList("Semester")%>
<%= Html.Hidden("strSemesterToBaseOn",returnedValueFromAbove)%>
<%: Html.ValidationSummary(true) %>
<input type="submit" value="Clone" />
<% } %>
<br/><br/>
Do I need to write the input tag of "submitt" 2 times or just only once?
Edit ( EXTRA CODE )
Controller's action method :
[HttpPost]
public ActionResult CloneSemesterData(string strSemesterToOrganize, string strSemesterToBaseOn)
{
.............................................................
..............................
}
HERE ( another Controller's method ) IS THE DROP DOWN LIST Filled with Semester values
public ActionResult DepartmentAdministration()
{
// Get list of semesters
var lr = new ListRepository();
ViewData["Semester"] = new SelectList(lr.ListSemester(3)); //this ListSemester(3) will generate the list with 3 strings ( e.g "WS 2012", "SS2010")
return View();
}
My View code in .aspx file is :
//this executes when radioButton ="Clone" is selected
<% using (Html.BeginForm("CloneSemesterData", "CourseNeededHours"))
{%>
<%= Html.DropDownList("Semester")%> // this is First drop down list box , from which selected value , I want to transfer as 1st parameter of controller's action method
<%: Html.ValidationSummary(true) %>
based On
<%= Html.DropDownList("Semester")%> //this is Second drop down list box, from which selected value, I want to transfer as 2nd parameter of controller's action method.
<input type="submit" value="Clone" />
<% } %>
ERROR:
Now, after fixing using Edit 2 : it is giving red lines under
as it is somehow not recognizing the ViewData["SemesterList"]...
"System.Web.Mvc.HtmlHelper does not contain a definition for 'DropDownList' and the best extension method overloaded 'System.Web.Mvc.Html.SelectExtensions.DropDownList(System.Web.Mvc.HtmlHelper, string,System.Collections.Generic.IEnumerable') has some invalid arguments".
Hope now it will clear, still ambiguity , do let me know then.
Regards
Usman
I am not really sure what you're asking here. You don't need any kind of hidden field to post the selected values of a dropdown. Your Dropdownlist code is invalid to begin with.
Typically you have something like this:
<%= Html.DropDownList("SemesterToOrganize", GetSemesterToOrganize()) %>
<%= Html.DropDownList("SemesterToBaseOn", GetSemesterToBaseOn()) %>
And in your controller:
[HttpPost]
public ActionResult MyAction(string SemesterToOrganize, string SemesterToBaseOn) {
// your code.
}
EDIT:
Based on what you've told us. You are relying on the behavior of MVC of populating the DropDownList because you are adding your list to the ViewData with the same name as your dropdownlist. This won't work for you. You will have to populate each dropdown list seperately.
In your controller, do something like this:
public ActionResult MyAction ()
{
ViewData["SemesterList"] = // list of semesters
return View();
}
Then, in your view you have:
<%= Html.DropDownList("SemesterToOrganize", ViewData["SemesterList"]) %>
<%= Html.DropDownList("SemesterToBaseOn", ViewData["SemesterList"]) %>
then your post method
[HttpPost]
public ActionResult MyAction(string SemesterToOrganize, string SemesterToBaseOn) {
// your code.
}
If you want to continue to argue that you can do it your way, then you won't solve your problem. Each dropdown must have it's own unique id, otherwise it will not post correctly. The only way to solve this problem is to give each it's own unique id. That breaks the behavior of the drop down automatically getting the data, so you MUST specify the list of data explicitly.
So stop arguing that this is an unimportant part of the problem. It's not. It's key to the problem.
EDIT2:
Based on your code above:
<%= Html.DropDownList("strSemesterToOrganize", (SelectList)ViewData["Semester"]) %>
<%= Html.DropDownList("strSemesterToBaseOn", (SelectList)ViewData["Semester"]) %>
That's all you need
If you had just given us this, and didn't argue, this would been solved a lot easier.
// Try this. Change names and put in the appropriate namespace.
//Your view
#model MvcApplication2.Models.CloneSemesterDataViewModel
#Html.LabelFor(x => x.SemesterToOrganize)
#Html.DropDownListFor(x => x.SemesterToOrganize, Model.ListofSemestersToOrganize)
--------
#Html.LabelFor(x => x.SemesterToBaseOn)
#Html.DropDownListFor(x => x.SemesterToBaseOn, Model.ListofSemestersToBaseOn)
//view model
namespace MvcApplication2.Models
{
public class CloneSemesterDataViewModel
{
public string SemesterToOrganize { get; set; }
public IEnumerable<SelectListItem> ListofSemestersToOrganize
{
get
{
return new List<SelectListItem> { new SelectListItem { Text = "SS2012" , Value = "SS2012"} };
}
}
public string SemesterToBaseOn { get; set; }
public IEnumerable<SelectListItem> ListofSemestersToBaseOn
{
get
{
return new List<SelectListItem> { new SelectListItem { Text = "SS2012", Value = "SS2012" } };
}
}
}
}
----------
Controller.
[HttpPost]
public ActionResult CloneSemesterData(CloneSemesterDataViewModel viewModel)
{
//viewModel.SemesterToBaseOn
//viewModel.SemesterToOrganize
}
// This should do the trick.

ASP.Net MVC2 Custom Templates Loading via Ajax and Model Updating

I have a view model with a collection of other objects in it.
public ParentViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public List<ChildViewModel> Child { get; set; }
}
public ChildViewModel
{
public int Id { get; set; }
public string FirstName { get; set; }
}
In one of my views I pass in a ParentViewModel as the model, and then use
<%: Html.EditorFor(x => x) %>
Which display a form for the Id and Name properties.
When the user clicks a button I call an action via Ajax to load in a partial view which takes a collection of Child:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<Child>>" %>
<%: Html.EditorFor(x => x) %>
which then uses the custom template Child to display a form for each Child passed in.
The problem I'm having is that the form created by the Child custom template does not use the naming conventions used by the DefaultModelBinder.
ie the field name is (when loaded by Ajax):
[0].FirstName
instead of:
Child[0].FirstName
So the Edit action in my controller:
[HttpPost]
public virtual ActionResult Edit(int id, FormCollection formValues)
{
ParentViewModel parent = new ParentViewModel();
UpdateModel(parent);
return View(parent);
}
to recreate a ParentViewModel from the submitted form does not work.
I'm wondering what the best way to accomplish loading in Custom Templates via Ajax and then being able to use UpdateModel is.
Couple of things to start with is that you need to remember the default ModelBinder is recursive and it will try and work out what it needs to do ... so quite clever. The other thing to remember is you don't need to use the html helpers, actual html works fine as well :-)
So, first with the Model, nothing different here ..
public class ParentViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public List<ChildViewModel> Child { get; set; }
}
public class ChildViewModel
{
public int Id { get; set; }
public string FirstName { get; set; }
}
Parent partial view - this takes an instance of the ParentViewModel
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<ParentViewModel>" %>
<h2>Parent</h2>
<%: Html.TextBox("parent.Name", Model.Name) %>
<%: Html.Hidden("parent.Id", Model.Id) %>
<% foreach (ChildViewModel childViewModel in Model.Child)
{
Html.RenderPartial("Child", childViewModel);
}
%>
Child partial view - this takes a single instance of the ChildViewModel
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<ChildViewModel>" %>
<h3>Child</h3>
<%: Html.Hidden("parent.Child.index", Model.Id) %>
<%: Html.Hidden(string.Format("parent.Child[{0}].Id", Model.Id), Model.Id)%>
<%: Html.TextBox(string.Format("parent.Child[{0}].FirstName", Model.Id), Model.FirstName) %>
Something to note at this point is that the index value is what is used for working out the unique record in the list. This does not need to be incremental value.
So, how do you call this? Well in the Index action which is going to display the data it needs to be passed in. I have setup some demo data and returned it in the ViewData dictionary to the index view.
So controller action ...
public ActionResult Index()
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
ViewData["Parent"] = GetData();
return View();
}
private ParentViewModel GetData()
{
var result = new ParentViewModel
{
Id = 1,
Name = "Parent name",
Child = new List<ChildViewModel>
{
new ChildViewModel {Id = 2, FirstName = "first child"},
new ChildViewModel {Id = 3, FirstName = "second child"}
}
};
return result;
}
In the real world you would call a data service etc.
And finally the contents of the Index view:
<form action="<%: Url.Action("Edit") %>" method="post">
<% if (ViewData["Parent"] != null) { %>
<%
Html.RenderPartial("Parent", ViewData["Parent"]); %>
<% } %>
<input type="submit" />
</form>
Saving
So now we have the data displayed how do we get it back into an action? Well this is something which the default model binder will do for you on simple data types in relatively complex formations. So you can setup the basic format of the action which you want to post to as:
[HttpPost]
public ActionResult Edit(ParentViewModel parent)
{
}
This will give you the updated details with the original ids (from the hidden fields) so you can update/edit as required.
New children through Ajax
You mentioned in your question loading in custom templates via ajax, do you mean how to give the user an option of adding in another child without postback?
If so, you do something like this ...
Add action - Need an action which will return a new ChildViewModel
[HttpPost]
public ActionResult Add()
{
var result = new ChildViewModel();
result.Id = 4;
result.FirstName = "** to update **";
return View("Child", result);
}
I've given it an id for easy of demo purposes.
You then need a way of calling the code, so the only view you need to update is the main Index view. This will include the javascript to get the action result, the link to call the code and a target HTML tag for the html to be appended to. Also don't forget to add your reference to jQuery in the master page or at the top of the view.
Index view - updated!
<script type="text/javascript">
function add() {
$.ajax(
{
type: "POST",
url: "<%: Url.Action("Add", "Home") %>",
success: function(result) {
$('#newchild').after(result);
},
error: function(req, status, error) {
}
});
}
</script>
<form action="<%: Url.Action("Edit") %>" method="post">
<% if (ViewData["Parent"] != null) { %>
<%
Html.RenderPartial("Parent", ViewData["Parent"]); %>
<% } %>
<div id="newchild"></div>
<br /><br />
<input type="submit" /> add child
</form>
This will call the add action, and append the response when it returns to the newChild div above the submit button.
I hope the long post is useful.
Enjoy :-)
Hmm... i personally would recommend to use a JSON result, instead of a HTML result, that you fiddle in the page...
makes the system cleaner. and your postback working ;-)
I found another way to accomplish this which works in my particular situation.
Instead of loading in a partial via via Ajax that is strongly typed to a child collection like:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<Child>>" %>
I created a strongly typed view to the parent type and then called EditorFor on the list like so:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Parent>" %>
<%: Html.EditorFor(x => x.ChildList) %>
This then calls a Custom Display Template and the result is that all the HTML elements get named correctly and the Default Model binder can put everything back together.

Modelbinding using Interfaces in ASP.NET MVC 2

I have the following View Data:
public class ShoppingCartViewData
{
public IList<IShoppingCartItem> Cart
{
get;
set;
}
}
I populate the viewdata in my controller:
viewData.Cart = CurrentSession.CartItems;
return View(viewData);
And send the data to the view and display it using:
<% for (int i = 0; i < Model.Cart.Count; i++ ) { %>
<%= Html.TextBoxFor(m => m.Cart[i].Quantity)%>
<%= Html.HiddenFor(m => m.Cart[i].Id) %>
<% } %>
I want to be able to catch the viewdata on the post. When I try:
[HttpPost]
public ActionResult UpdateCart(ShoppingCartViewData viewData)
{
...
}
When I run this I get a: System.MissingMethodException: Cannot create an instance of an interface.
Can anyone shed some light on this. What would I have to do to get this to work?
Many Thanks
You could try adding the formcollection as a parameter. And shouldn't viewdata be the viewmodel you're using?
[HttpPost]
public ActionResult UpdateCart(ShoppingCartViewModel viewModel, FormCollection collection)
{
...
}
Not sure if this is the exact solution, i'm also busy learning MVC2.0 and .NET4 ;-)
I'd create a model binder for your ViewModel, and then you can instantiate a concrete type that implements the appropriate interface when it binds to the method parameters.
You can insert logic into your model binder to read the form fields as appropriate and then instantiate the right IList or IShoppingCartItem data, so no need to worry about being pinned to a single implementation of the interface either.
Given my two comments this is how I would do it:
// you don't need this
// viewData.Cart = CurrentSession.CartItems;
// return View(viewData);
// do it like this
return View(CurrentSession.CartItems);
Then have a strongly typed View either this:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Administration.Master" Inherits="System.Web.Mvc.ViewPage<ShoppingCartViewData>" %>
or this:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Administration.Master" Inherits="System.Web.Mvc.ViewPage<List<IShoppingCartItem>>" %>
Also this code won't work. This will generate you a bunch of textboxes with the same name and id. You need to generate textboxes with a count and for that you won't be able to use
Html.TextBoxFor(). You will have to revert to Html.TextBox() or create a new extension TextBoxFor() method which would also accept a number (for you count).
<% for (int i = 0; i < Model.Cart.Count; i++ ) { %>
<%= Html.TextBoxFor(m => m.Cart[i].Quantity)%> // this won't work, if you want to post back all textboxes after they are edited
<%= Html.HiddenFor(m => m.Cart[i].Id) %>
<% } %>
HTH
I got the same exception after adding an 'int' parameter to an action method.
I discovered by putting a break point in the controllers constructor that one of the other action methods (not the one specified in the forms post arguments) was being called instead.

Binding to a SelectList in MVC

Once again I'm confronted with a "This shouldn't be this ?*!# hard" situation.
Problem: I want to use a form in MVC for creation of an object. One of the elements of the object is a set of limited choices - a perfect candidate for a drop down list.
But if I use a SelectList in my model, and a drop down list in my View, and then try to post the Model back to my Create method, I get the error "Missing Method Exception:No Parameterless constructor for this object". Exploring the MVC source code, it appears that in order to bind to a model, the Binder has to be able to create it first, and it can't create a SelectList because there is no default constructor for it.
Here's the simplified code:
For the model:
public class DemoCreateViewModel
{
public SelectList Choice { get; set; }
}
For the controller:
//
// GET: /Demo/Create
public ActionResult Create()
{
DemoCreateViewModel data = new DemoCreateViewModel();
data.Choice = new SelectList(new string[] { "Choice1", "Choice2", "Choice3" });
ViewData.Model = data;
return View();
}
//
// POST: /Demo/Create
[HttpPost]
public ActionResult Create(DemoCreateViewModel form)
{
try
{
// TODO: Add insert logic here
return RedirectToAction("Index");
}
catch
{
return View();
}
}
And for the View:
<fieldset>
<legend>Fields</legend>
<%= Html.LabelFor(model => model.Choice) %>
<%= Html.DropDownListFor(model => model.Choice, Model.Choice) %>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
Now, I know I can MAKE this work by dropping back 10 yards and punting: bypass model binding and drop back to the FormCollection and validate and bind all the fields myself, but there's got to be a simpler way. I mean, this is about as simple a requirement as it gets. Is there a way to make this work within the MVC ModelBinding architecture? If so, what is it? And if not, how come?
Edit: Well, I have egg on my face, but maybe this will help someone else. I did some more experimenting and found a simple solution that seems to work.
Provide a simple value (string or integer, depending on what your select list value type is), and name that as the model element that you bind to. Then provide a second element as the select list of choices, and name it something else. So my model became:
public class DemoCreateViewModel
{
public string Choice { get; set; }
public SelectList Choices { get; set; }
}
And then the DropDownListFor statement in the View becomes:
<%= Html.DropDownListFor(model => model.Choice, Model.Choices) %>
When I do this, the submit button correctly binds the choice made in the form to the string Choice, and submits the model back to the second Create method.
Here is one approach:
#Html.DropDownListFor(model => model.Choice,
ViewBag.Choices as SelectList,
"-- Select an option--",
new { #class = "editor-textbox" })
Notice that I use ViewBag to contain my SelectList. This way when you post back, the client doesn't send the entire select list up to the server as part of the model.
In your controller code, you just need to set the view bag:
ViewBag.Choices = new SelectList(....
Consider creating a different view model for your post action without the SelectList property:
public class DemoCreateViewModelForUpdate
{
public string Choice { get; set; }
}
Then you can always map from the DemoCreateViewModelPost instance to an DemoCreateViewModel instance if the model state is invalid and you want to re-show the view. I tend to prefer everything needed by the view to be in my display view model class, so using a separate update only view model let's me keep things slim and trim for the trip back to the server.
In your view, you'd do:
#Html.DropDownListFor(m => m.Choice, Model.Choices)
as in the previous answer, so no unnecessary data would round trip.

Actionresult doesnt get called by routelink. Formcollection the culprit?

I am fairly new to MVC. I am trying to set up a search page that searches a database and returns results. The search box is within a Html.BeginForm in my View, and looks like this:
<% using (Html.BeginForm())
{ %>
<%= Html.TextBox("searchBox", null, new { #id = "searchBox" })%>
<div id="searchButtonsDiv">
<input type="submit" value="Search" />
</div>
<% } %>
//Results are returned in a ul and orgainized
//Pagination below
<% if (Model.HasPreviousPage)
{ %>
<%= Html.RouteLink("Previous", "SearchResults", new { page = (Model.PageIndex - 1) })%>
<% } %>
<% if (Model.HasNextPage)
{ %>
<%= Html.RouteLink("Next", "SearchResults", new { formCollection = "", page = (Model.PageIndex + 1) })%>
<% } %>
I am using a FormCollection to pass to my controller that looks like this:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(FormCollection formCollection, int? page)
{
var searchString = formCollection["searchBox"];
var results = resultsRepository.GetResults();
var paginatedResults = new PaginatedList<Driver>(results, page ?? 0, pageSize);
return View(paginatedResults);
}
So far so good. When I type a word and press the submit button, Index gets called and the database returns accordingly. The ul gets populated with the results, and when there are more than pageSize results (10 in my case), the Next link shows up.
When I click "Next", the default page just loads. No pagination or anything like that. I'm pretty sure it has to do with the fact that my Index ActionResult has a FormCollection as a paramater. I thought I read somewhere that only strings/ints can be handled? Here is the MapRoute:
routes.MapRoute(
"SearchResults",
"Drivers/Index/{formCollection}/{page}",
new { controller = "Drivers", action = "Index", formCollection = "", page = "" }
);
Am I completely missing something or is there a way to handle this? I know I could just use jquery/ajax to send the string contained in the search listbox, but I don't want to do that because later I plan on adding checkbox's as means of filtering searches, etc.
I tried several different ways of setting the formCollection's value, including creating a new FormCollection that adds the searchBox, and just passing strings, etc.
The FormCollection argument in the action isn't the problem. That will always work.
It absolutely does not belong in your route, however! Just get rid of that and you'll probably solve the problem. Form elements don't go in the URI, and only stuff in the URI should be in the route.
It's not how I'd write that action signature, however. I'd suggest:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(string searchBox, int? page)
{
var results = resultsRepository.GetResults();
var paginatedResults = new PaginatedList<Driver>(results, page ?? 0, pageSize);
return View(paginatedResults);
}
Finally: You shouldn't return a View from a POST in this case. This will cause weird behavior for the user; e.g., when they press refresh their browser will warn them about re-submitting the form.
You should either:
Use a GET, not a POST for search results.
Redirect instead of returning a view.
I'd pick the first, personally.

Resources