When is goto edit data screen apply a model to fill data if user do make some changes but press back button the model saves the data? I am assigning the model to another demo model but it reflects the changed values.
You can create copy method in reference class and assign it's property on back action.
Override your model init method like below.
required override init() {
}
required init(_ model: Person) {
// Assign your Values
}
Use it Like
let obj = People(model : Person)
to copy
Either you can implement copyable protocol in your class model and assigning copy of that model.
or
Create struct instead of class.
Related
Suppose I have:
class User extends Model {
...
public function sendFriendRequestTo($user){
// $user represents another User instance
}
}
My guess is that it is better to always pass in simply the id of the Model object and retrieve the record corresponding to that id from inside the function. This ensures that our data is synchronized.
Is there such a thing as a best practice in this regard?
As OOP dependency inversion principle (D in SOLID) said you should inject your dependency in that method and i think inject that instance is better way.
I have 2 questions.
First=> How can i pass value between 2 ViewModels ?
For example, I m adding data and showing it into the MainPAGE, and simultaneously I want to show the same data ( Observable Collection ) in the ChildPAGE too. Inside the ChildPAGE Xaml I assigned the BindingContext to ViewModel and I assigned listview’s data source to that Observable Collection .But I couldn’t make it work . I tried some examples, but I couldn’t manage it to work. If the data load in the ChildPAGE’s constructor then it works else doesn’t work. I thought , I would improve the performance with using one ObservableCollection, but I think, the mechanism in MVVM is different.
So how can I use one ObservableCollection in 2 pages.
Second => How can I pass data between ViewModels without using the constructor.
Example: I Have 2 Pages ( MainPage and ChidPage) and 2 ViewModels ( MainVM and ChildVM ) .
Situation => If I would pass data from MainPage to ChildPage , I would send data within the constructor .But I want to get data from Childpage to MainPage . So PopAsync doesn’t have a constructor. I also tried EventHandler but it doesn’t work.
Is the only solution is Messaging center? Or what do you advice for better performance? Also does the MessagingCenter reduces the performance because of high usage of RAM?
NOTE: ( I want to learn the mvvm architecture, so I don’t want to use other MVVM Frameworks. I want to get the idea of MVVM and C# comprehensively.)
Thanks in advance
You could create a model class that both view models depend on. The model itself would hold the observable collection, while the view models reference the observable collection. This is a good way to share data across various view models throughout the life of your app. Usually you will want to make the model a singleton to ensure that it really is the same data.
If you don't want to use messaging center, you can use an MVVM framework which comes with other benefits. I use Prism to simplify navigation and you can pass navigation parameters along.
Finally, and this is not the best option usually, you may maintain data in the App object. You can set values in the Properties collection. This is not advisable for complex objects.
EDIT
First you would have some data transfer object which the ObservableCollection would contain, unless you're just holding integers or something.
public class MyDTO
{
//fields for Data Transfer Object
}
DTO's are often among your models, but sometimes you need a composite class to hold DTO's and collections of DTO's
Here is a simple Model that would contain your ObservableCollection
public class MyCollectionModel
{
#region Singleton Pattern
private MyCollectionModel()
{
}
public static MyCollectionModel Instance { get; } = new MyCollectionModel();
#endregion
private ObservableCollection<MyDTO> _dtos;
public ObservableCollection<MyDTO> MyObservableCollection
{
get { return _dtos; }
set { _dtos = value; }
}
}
Note that this implements the Singleton pattern. It could even implment INotifyPropertyChanged as well.
Next your view models, imagine a MyVM1 and MyVM2, would reference the ObservableCollection in your Model with something like this.
public class MyVM1 : INotifyPropertyChanged // Do the same with MyVM2, which would be the binding context for view 2
{
private MyCollectionModel _model;
public MyVM1 ()
{
_model = MyCollectionModel.Instance;
}
private ObservableCollection<MyDTO> myVar;
public ObservableCollection<MyDTO> MyProperty //Bind to this in your View1
{
get
{
return _model.MyObservableCollection;
}
set
{
myVar = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I'm leaving quite a few things out here, and don't really have time to build an entire project just to test. I hope this helps.
You normally use await navService.PushAsync(new MyPage2(dataToShare)); to pass data between pages/VMs. This involves using constructors. Here is a simple example of it: https://stackoverflow.com/a/47873920/1508398
Since you don't want to use constructors, you may load the data on your child page from your service. Alternatively, you may cache the data on the local storage (client-side) and simply load it from there in your child/main page.
As per docs, kotlin var properties can be bind in ViewModel as
// Kotlin var property
class PersonVarViewModel(person: Person) : ViewModel() {
val name = bind { person.observable(Person::name) }
}
It seems like doesn't work.
How to solve this issue. IDE shows red underline bellow "bind"
but if i write
val name = bind(RoomType::name)
it shows no error. but updating the value using UI fields does'nt update the model value.
Please help
In your class declaration, use var person: Person.
person needs to be a member of the class, not just a parameter to the constructor. You can do this by declaring it var or val in the constructor parameters, or you can add a member field to the class the conventional way and assign it (probably using by property, but not sure if that's what you want)
class PersonVarViewModel(var person: Person) : ViewModel() {
val name = bind { person.observable(Person::name) }
}
For ItemViewModel ...
class PersonVarViewModel(var person: Person) : ItemViewModel<Person>() {
val name = bind { person.observable(Person::name) }
}
You need to make the ItemViewModel aware of the person instance, but also let it react to changes to the underlying item later. You need to assign the person you pass in to the item property of the ItemViewModel. This can be done by passing it in the constructor:
class PersonVarViewModel(person: Person) : ItemViewModel<Person>(person) {
val name = bind(Person::name)
}
Be aware that if you add this constructor, you can only use the that viewmodel with injection if you push it manually into scopes, since it can't be instantiated by the framework. You should therefore either add a noargs constructor as well, or simply omit the person parameter and assign to item after you create it.
If you update the value in the underlying person, it will only be visible in the view model if the value is observable. If not, you have to call rollback() to update changes from the person. You can call rollback for specific fields only.
If possible, use observable properties in your domain model objects to avoid such issues.
I have a view model sent to the edit action of my controller. The ViewModel contains references to EntityObjects. (yea i'm fine with it and don't need to want to duplicate all the entities properties in the viewmodel).
I instantiate the view model and then call UpdateModel. I get an error that a property is "null" which is fine since it is a related model. I am trying to exclude the property from being bound during model binding. On debugging it I see in the entity where the model binder is trying to set the value of the property to null.
Here is my edit action:
var model = new SimplifiedCompanyViewModel(id);
var excludeProperties = new string[] {
"Entity.RetainedEarningsAccount.AccountNo"
,"Property.DiscountEarnedAccount.ExpenseCodeValue"
,"Entity.EntityAlternate.EntityID"
,"Property.BankAccount.BankAccountID"
,"Entity.PLSummaryAccount.AccountNo"
,"Property.RefundBank.BankAccountID"
,"Company.Transmitter.TCC"
};
try
{
UpdateModel<SimplifiedCompanyViewModel>(model, String.Empty, null, excludeProperties);
if (ModelState.IsValid)
{
//db.SaveChanges();
}
return RedirectToAction("Index");
}
catch
{
return View(model);
}
I have looked at a few other issues about specifying a "prefix" but I don't think that is the issue since I am telling it to bind to the viewmodel instance not just the entity object.
Am I excluding the properties correctly? Strange thing is is only seems to happen on this item. I suspect it may be an issue with the fact that there is actually no refund bank related to my entity. But I have other related items that don't exist and don't see the same issue.
More info... since I'm told me model isn't designed well.
The Company is related to a BankAccount. The Company view shows the currently related BankAccount.BankAccountId and there is a hidden field with the BankAccount.Key. I use jQueryUI autocomplete feature to provide a dropdown of bank account displaying the BankAccount.BankAccountId and when one is selected the jQuery code changes the hidden field to have the correct Key value. So, when this is posted I don't want the current bankaccounts BankAccountID modified, hence I want it to skip binding that field.
If I exclude BankAccountId in the model then on the BankAccount edit view the user would never be able to change the BankAccountId since it won't be bound. I'm not sure how this indicates a poor model design.
Use the Exclude property of the Bind attribute:
[Bind(Exclude="Id,SomeOtherProperty")]
public class SimplifiedCompanyViewModel
{
public int Id { get; set; }
// ...
}
This is part of the System.Web.Mvc namespace. It takes a comma-separated list of property names to exclude when binding.
Also you should consider using TryUpdateModel instead of UpdateModel. You can also just have the default model binder figure it out by passing it as an argument to the constructor:
public ActionResult Create([Bind(Exclude="Id")]SimplifiedCompanyViewModel model)
{
// ...
}
A very simple solution that I figured out.
try
{
UpdateModel<SimplifiedCompanyViewModel>(model, String.Empty, null, excludeProperties);
ModelState.Remove("Entity.RetainedEarningsAccount.AccountNo");
ModelState.Remove("Property.DiscountEarnedAccount.ExpenseCodeValue");
ModelState.Remove("Entity.EntityAlternate.EntityID");
ModelState.Remove("Property.BankAccount.BankAccountID");
ModelState.Remove("Entity.PLSummaryAccount.AccountNo");
ModelState.Remove("Property.RefundBank.BankAccountID");
ModelState.Remove("ompany.Transmitter.TCC");
if (ModelState.IsValid)
{
//db.SaveChanges();
}
return RedirectToAction("Index");
}
catch
{
return View(model);
}
Another option here is simply don't include this attribute in your view and it won't be bound. Yes - you are still open to model injection then if someone creates it on the page but it is another alternative. The default templates in MVC will create your EditorFor, etc as separate items so you can just remove them. This prevents you from using a single line view editor with EditorForModel, but the templates don't generate it that way for you anyways.
EDIT (adding above comment)
DRY generally applies to logic, not to view models. One view = one view model. Use automapper to easily map between them. Jimmy Bogard has a great attribute for this that makes it almost automatic - ie you create the view model, load up your Customer entity for example, and return it in the action method. The AutpMap attribute will then convert it to a ViewModel. See lostechies.com/jimmybogard/2009/06/30/how-we-do-mvc-view-models
Try the Exclude attribute.
I admit that I haven't ever used it.
[Exclude]
public Entity Name {get; set;}
I've this scenario in my application:
Controller1
.GetItems()
Model1
.GetItems()
Controller2
.GetCurrentUser()
Model2
.CurrentUser
In this scenario Controller1.GetItems() calls Model1.GetItems() method. Model1.GetItems() method needs to know (for example) what is the role of the current user to build the correct list of items, and it should get it from the Model2.CurrentUser property (that stores the cached information about the current user).
Is it a good practice to acces a model from another model?
Thanks,
Regards
You are going to run into some arguments about the best way to do this, but at the end of the day, you have two options. Either you can have the model pull the information it needs from the other model or you can have the controller pass the information needed.
Based upon what I have read, as long as the model does not have any controller logic or view logic you are good so there is nothing wrong with having the model know about other models. However, others have argued that having the controller pass the information that is needed makes the code a bit easier to document since you can see that the model requires information from somewhere else. At the end of the day though, I see both as being valid and which one you choose to use will likely come down to personal preference.
Design - Controller Provides Data
ModelOne
User GetCurrentUser()
ModelTwo
Items[] GetItems(User)
Snippet - Controller Proivdes Data
Controller {
function doWork() {
User user = ModelOne.GetCurrentUser();
Items[] items = ModelTwo.GetItems(user);
}
}
Design - Model Gets Data
ModelOne
User GetCurrentUser()
ModelTwo
Items[] GetItems()
Snippet - Model Gets Data
ModelTwo {
Items[] GetItems() {
User user = ModelOne.GetCurrentUser();
...
}
}
Controller {
function doWork() {
Items[] items = ModelTwo.GetItems();
}
}
If your second model is going to referenced often by your first, you could compile your model classes into a separate library?
I think you shouldn't have references between models, the right way is to use the controller for this link. So in your case you could pass the CurrentUser as a parameter in the GetItems method.