Hey all new to the MVC world so I'm sure I am not doing something correct here. I am trying to call a function from the model page from my view index page to populate some tooltips.
In my model:
public class tips
{
public List<string> allTips()
{
List<string> theTips = new List<string>();
theTips.Add("example 1");
theTips.Add("example 2");
return theTips;
}
private List<string> _tips;
public List<string> getTips { get { return _tips; } set { _tips = allTips(); } }
}
And in my view:
public ActionResult Index()
{
var blah = new tips().getTips;
ViewBag.pageTips = blah;
return getMainData();
}
And then I have this on the razor page:
#Html.Raw(ViewBag.pageTips[1])
Which should display example 2 on the page but instead it just displays null as the value for the toolTips.
Currently it has a value of null when it gets to the return for the pageTips in my view.
So what would I be doing incorrectly? I put some stops here and there and notice that it never calls the allTips() function so that's a good starting place as to what I need to do in order to do that.
I just figured that calling the .getTips would fire off the theTips function?
You seem to be confusing constructors with properties in your model, which is very strange. I suspect you want something like this instead:
public class TipsObj
{
// a property
public List<string> Tips { get; set; }
// a constructor
public TipsObj()
{
Tips = new List<string>();
Tips.Add("example 1");
Tips.Add("example 2");
}
}
The idea here is that the constructor is called by default when you create a new instance of the object, so your Tips property will automatically be populated right away just by creating an instance of this.
Then in your controller action you can simply do this:
var tips = new TipsObj();
ViewBag.pageTips = tips.Tips;
Which will create an instance of the TipsObj and set a ViewBag property to the Tips property of that instance. Which was initialized in the constructor of that object.
Note that none of this really has anything to do with MVC, this is just creating an object in C# and using a property on that object.
Note also that I changed some of the names here. In C# class names should begin with a capital letter. Also, you don't want to call everything "tips" (and you don't want to call anything "blah"). Using meaningful and intuitive names makes your code easier to understand, which will help you understand what you're writing.
You are misunderstanding the concept of setter and using it as an "initializer", a setter is meant to set the value, to change it in other word. if you want to initialize it do it in the constructor.
Here you are using two different Lists, I don't really know why.
A working code would be:
public class tips
{
public tips()
{
_tips = new List<string>();
_tips.Add("example 1");
_tips.Add("example 2");
}
private List<string> _tips;
public List<string> getTips { get { return _tips; } set { _tips = value; } }
}
Then in the controller:
ViewBag.pageTips = new tips().getTips;
Then call it this way in the view:
#Html.Raw(ViewBag.pageTips[1])
Change your model like the following:
public class Tips
{
private List<string> _tips {get; set;}
public Tips()
{
_tips = new List<string>();
_tips.Add("example 1");
_tips.Add("example 2");
}
public List<string> getTips()
{
return _tips;
}
}
Then use it :
ViewBag.pageTips = new Tips().getTips();
There are a few things wrong here, but I'll start with answering your question directly... the reason ViewBag.pageTips[1] is null is because you never initialize the _tips array in your model. The only code that would do that is in the getTips property's setter, which is never invoked. If you attach a debugger it'll be apparent :)
You could refactor this in a few ways, including changing the allTips() method to a constructor, and initializing your collection there.
I don't want to put an example of that directly though. You mentioned you were new to MVC, so I want to show you how this should really be done. One of the big benefits of MVC is Model Binding, so forget about ViewBag.
Try this instead:
The Model
using System.Collections.Generic;
namespace WebApplication1.Models
{
public class TipsModel
{
public List<string> Tips { get; }
public TipsModel()
{
Tips = new List<string> {"example 1", "example 2"};
}
}
}
The Controller
public ActionResult Index()
{
var model = new TipsModel();
return View(model);
}
The View
#model WebApplication1.Models.TipsModel
#{
ViewBag.Title = "Index";
}
#Model.Tips[1]
Related
I have a question, that I tried to Google but honestly, I don't really know how to search or even ask this particular question.
Let's imagine I have the following:
Controller
[HttpGet]
public virtual ActionResult Summary()
{
var summaryViewModel = new CheckoutSummaryViewModel()
{
Products = ProductViewModel.BuildListFromShoppingCart(GetShoppingCart())
};
return View("_Summary", summaryViewModel);
}
ProductViewModel
public class ProductViewModel
{
public string Name
{
get; set;
}
public static List<ProdutoCheckoutViewModel> BuildListFromShoppingCart(ShoppingCart shoppingCart, IMappingService mappingService)
{
var itemsInCart = new List<ProductViewModel>();
foreach (var item in shoppingCart.ItemsInCart)
{
var itemViewModel = mappingService.Map<Product, ProductViewModel>(item.Product);
itemViewModel.Quantidade = item.Quantity;
itemsInCart.Add(itemViewModel);
}
return itemsInCart;
}
}
This is not production code. Is just so I can explain what I mean.
Is this the correct way of doing this?
Is there a better way than using static for building the list? I really don't want to do it inside the controller.
Passing IMappingService to the method does not look right. But maybe I'm just being picky. Is it the best way?
Another case, where I need to pass Session State to a static helper class.
public static Guid GetCheckoutId(HttpSessionStateBase session)
{
return (Guid)session["checkoutId"];
}
Or, also, sometimes I need to pass as parameter, to helper methods, my "unifOfWork", since I use the repository pattern.
I've come accross this "problem" a lot and I did not find the best way, yet, to do it.
PS: If any of you has a better title for this question, please tell me so I can update it.
Controller
If you use DI, it would look something like this:
public class CheckoutController
{
private readonly ICheckoutService _checkoutService;
public CheckoutController(ICheckoutService checkoutService) =>
_checkoutService = checkoutService;
[HttpGet]
public virtual ActionResult Summary()
{
var shoppingCartData = _checkoutService.GetShoppingCart(Session["..."]);
// The viewmodel here could be returned by your service or the service
// would return all required data and the viewmodel simply transforms that Dto into what is needed by the UI
var summaryViewModel = new CheckoutSummaryViewModel()
{
Products = shoppingCartData
};
return View("_Summary", summaryViewModel);
}
}
We've got a WPF app with a landing page that lists about a dozen or so buttons, all going to new views/viewmodels of that type. Its becoming unwieldy. We've got one viewmodel that lists all of these which basically look like this:
private void ExecuteViewProgramCommand()
{
OpenViewMessage message = new OpenViewMessage();
CurrentViewModel = message.ViewModel = ViewModelLocator.ProgramVM;
Messenger.Default.Send<OpenViewMessage>(message);
}
I've never liked how this was done, as it violates the DRY principle. The only thing that changes in the the above code in the second line, where in this code what changes is ViewModelLocator.ProgramVM. I've been tasked with redoing the landing page, making it more organized and we're going to be adding more launching buttons. I think it would be better to use dependency injection. Also I'm trying to address the need to redesign the display, so that its in a list, rather than buttons scattered about, and in alphabetical order.
First I came up with this class:
public class Tile
{
public string ModuleName { get; set; }
public NamedViewModelBase ModuleViewModel { get; set; }
}
(NamedViewModelBase is the name of the viewmodel that's common to all of the viewmodels.) Then I declared a unit test to test this and declared this within the unit test:
List<Tile> tiles = new List<Tile>()
{
new Tile()
{
ModuleName = "Program",
ModuleViewModel = ViewModelLocator.ProgramVM
},
new Tile()
{
ModuleName = "Organization",
ModuleViewModel = ViewModelLocator.OrganizationVM
}
}
But this quickly became apparent that this was wrong. The assigning in the setter of ViewModelLocator.ProgramVM would instantiate the viewmodel for Program. I don't want that, I'd rather have the calling of instantiating it, such as we have in the ViewModelLocator:
static public ProgramViewModel ProgramVM
{
get
{
if (ServiceLocator.IsLocationProviderSet)
{
SimpleIoc ioc = ServiceLocator.Current as SimpleIoc;
return ioc.GetInstanceWithoutCaching<ProgramViewModel>(Guid.NewGuid().ToString());
}
else
{
return null;
}
}
}
So, I'm thinking that I've got to change the Tile class to declare the ModuleViewModel property to something like this: public NamedViewModelBase ModuleViewModel { get; }. But I don't know how I'd instantiate it when defining a List. What is the correct way to resolve this?
This is going to be psuedo codish advice which is kind of on the same track where you already are:
Assuming BaseViewModel is the base class for all your individual VM's
Create a Dictionary<string, BaseViewModel>
Fill this dictionary up during Application Start time (would look like your tiles List)
public void PreCreateVMs()
{
dictionary[Key] = new ConcreteViewModelType();
// Keep adding New Vms here
}
In the xaml, bind all your buttons to same Command which takes a string argument (or improvise this with Enum). Pass the correct String Key for each button.
Like: Accounts Button click should launch AccountVM which is stored with "AccountVM" key in the dictionary.
In the Command Handler - use the string, lookup the Dictionary find the correct ViewModel and Assign this object to CurrentViewModel
From maintenance point of view - all you need to add a new ViewModel is to update xaml with a new button, assign correct command parameter string. Use this string key and add the correct VM in the PreCreateVMs method.
I've redesigned the Tile class. What I believe I need is for the second parameter to be a Command. I'm asking if this might do better. Here's the new definition of Tile and an example of how I tried to implement it:
public class Tile
{
public string ModuleName { get; set; }
//public NamedViewModelBase ModuleViewModel { get; set; }
public Action ThisCommand { get; set; }
}
And here's how I tried to implement it as a List:
List<Tile> tiles = new List<Tile>()
{
new Tile()
{
ModuleName = "Program",
ThisCommand = () =>
{
if (ServiceLocator.IsLocationProviderSet)
{
SimpleIoc ioc = ServiceLocator.Current as SimpleIoc;
ioc.GetInstanceWithoutCaching<ProgramViewModel>(Guid.NewGuid().ToString());
}
}
},
new Tile()
{
ModuleName = "Organization",
ThisCommand = () =>
{
if (ServiceLocator.IsLocationProviderSet)
{
SimpleIoc ioc = ServiceLocator.Current as SimpleIoc;
ioc.GetInstanceWithoutCaching<OrganizationViewModel>(Guid.NewGuid().ToString());
}
}
}
};
Am I on the right track? Should I define tiles as a Dictionary instead?
This is my viewtrainees.cshtml.cs file snippets that contain the array code:
public class viewTraineeModel : PageModel
{
public string[] traineeinfo { get; set; }
(...)
In my OnGet():
string[] traineeinfo = new string[Convert.ToInt32(countnum)];
int i = 0;
while (trainees.Read())
{
traineeinfo[i] = trainees["t3_win"].ToString();
i++;
}
So everything is set in my array fine, I even tested it within the .cs file. However, when I pass the array or think I pass the array to the view it ALWAYS gives me an internal error.
I have tried on my viewtrainees.cshtml file:
#Model.traineeinfo[0]
And I get NullReferenceException: Object reference not set to an instance of an object. Error.
The head element of my code looks like this.
#page
#model T3_Landing.Pages.viewTraineeModel
#{
ViewData["Title"] = "View Trainees";
string[] traineeinfo = Model.traineeinfo as string[];
}
I have tried it with and without re-initializing the string array as seen above. I really want this to work.
Just a note I can pass regular strings just fine using #Model.examplestring but never an array, list, or object.
It's hard to tell without the full context, but it looks like you're hiding the class member by declaring a local variable within OnGet():
string[] traineeinfo = new string[Convert.ToInt32(countnum)];
This creates a local variable named traineeinfo that hides the class property (if that's the case I would expect a compiler warning). Since the class property is not set, you get a null reference exception in your Razor page.
Try just
traineeinfo = new string[Convert.ToInt32(countnum)];
I would also recommend using camel case to make it easier to read:
public string[] TraineeInfo { get; set; }
....
TraineeInfo = new string[Convert.ToInt32(countnum)];
Here on constructor, I think you may initialize string array for prevent null value.
public class viewTraineeModel : PageModel
{
public string[] traineeinfo { get; set; }
...
public viewTraineeModel()
{
traineeinfo=new string[0];
}
Please drop a screen shot or code snippet of how you're passing the data to your view from your controller method end. That will determine if you have the data you want to work with in your view in the first place.
I created a view model of all lists
namespace XXX.ViewModels
{
public class AlertData
{
public virtual List<String> lstPresent { get; set; }
public virtual List<String> lstTardy { get; set; }
public virtual List<String> lstNoShow { get; set; }
}
}
Then in my controller I want to do something like this:
private List<XXX.ViewModels.AlertData> dataToBeReturned()
{
…code to load lists here…
Then I create a view model and assigned new data to it:
var viewModel=new AlertData();
viewModel.lstPresent=present;
viewModel.lstTardy=tardy;
viewModel. lstNoShow = nowShow;
Finally I want to return like this:
Return viewModel();
It is here that I get an error message that says ‘viewModel’ is a variable but is used like a ‘method’. I searched but could not find a problem quite like mine.
Why am I getting error: ‘viewModel’ is a variable but is used like a ‘method’?
Thanks for any help with this one.
The viewModel that you're returning on the last line is actually a variable (an object that you instantiated), so you can simply return it without the parenthesis:
return viewModel;
Because viewModel is a variable. You're using it like a method.
When you call a method.. you pass arguments in parenthesis, like this:
someMethod(arg1, arg2);
You're trying to do that.. with your viewModel variable:
viewModel();
This is incorrect. Just return the view model:
return viewModel;
// ^^ no parenthesis
Remove the parenthesis:
return viewModel;
When you include the parentheses, it thinks you're trying to return the result of a method called viewModel(), which doesn't exist.
Interface IView
{
List<string> Names {get; set;}
}
public class Presenter
{
public List<string> GetNames(IView view)
{
return view.Names;
}
}
var mockView = MockRepository.GenerateMock<IView>();
var presenter = new Presenter();
var names = new List<string> {"Test", "Test1"};
mockView.Expect(v => v.Names).Return(names);
Assert.AreEqual(names, presenter.GetNames(mockView)) // Here presenter returns null which is incorrect behaviour in my case;
When I use the above code to return the mock list of names ,it doesn't match the expecatation then returns null and fails
thanks for your help
Edit:
I am passing the view as the paramter to presenter's GetNames method.Here the problem is when i return list object from the mocked property it returns null. However when i change the property data type to string/int i.e.premitive type then value is returned correctly
I don't see anywhere where your mockView is getting attached to your presenter. So from the presenter's point of view, the view is null. You might have to do something like:
presenter.View = view;
I just coded this with NUnit and RhinoMocks 3.5 to make sure it works. Here's my two class files. The test passed.
using System.Collections.Generic;
namespace Tests
{
public interface IView
{
List<string> Names { get; set; }
}
public class Presenter
{
public List<string> GetNames(IView view)
{
return view.Names;
}
}
}
using System.Collections.Generic;
using NUnit.Framework;
using Rhino.Mocks;
namespace Tests
{
[TestFixture]
public class TestFixture
{
[Test]
public void TestForStackOverflow()
{
var mockView = MockRepository.GenerateMock<IView>();
var presenter = new Presenter();
var names = new List<string> {"Test", "Test1"};
mockView.Expect(v => v.Names).Return(names);
Assert.AreEqual(names, presenter.GetNames(mockView));
}
}
}
I can only guess you are doing something wrong with the way you've mixed up your code.
Thanks for your help, after investigating I found that I was creating a new list object inside the presenter with the same content of view list object, and because of this it was failing.
Now I used the property constraints to match the parameters in expectation and it worked!!
Thanks all
I'm not familiar with Rhino Mocks but I can tell you how to do this with NUnit's built-in mock library, NUnit.Mocks:
List names = new List {"Test", "Test1"};
DynamicMock mockView = new DynamicMock(typeof(IView));
mockView.ExpectAndReturn("get_Names", names);
IView view = (IView)mockView.MockInstance;
Assert.AreEqual(names, presenter.GetNames(view));
One thing you should not forget (I know I did and it got me confused): specify how many times you want the expectation to work - otherwise if your code uses the property more than once, you will get weird results, since the expectation
mockView.Expect(v => v.Names).Return(names);
works for a single call only. So you should write
mockView.Expect(v => v.Names).Return(names).Repeat.Any();
if your mocked property is supposed to return the same stuff every time it's called.