Durandal: Accessing one view model from another - viewmodel

Using Durandal, I have two view models within my app, let's say vmCompanies & vmEmployees. I have two views, one for each view model and within each, you can see all companies & all employees.
However, when I'm loading my employees, in the DB they have an ID for which company they are employed by. What I'd like to do is the following pseudo:
From within the vmEmployees, get a reference to the vmCompanies
If the vmCompanies has already been initialized (which I know it is 99% of the time), get a reference to it so I can use something like linq.js to find the specific company this employee works for
If the vmCompanies has not been initialized (aka: activated), do so
This way I can avoid the requirement of the vmEmployees having it's own internal cache of companies. So far I've been unable to figure out how in Durandal to query and ask "give me this view model that you've already loaded." It seems like it has that internally because when I navigate between views, they are cached and not reloaded (same with the VMs)... I've just so far been unable to see how I can do it.

You can manually require() a view model by its ____moduleId____. As long as your view model module returns an object and not a function, you'll be dealing with a singleton, so you can be sure you'll get the correct instance.
If you're not sure what the __moduleId__ is, you can use this chrome extension to view your view model's properties, including __moduleId__.
However, instead of manually instantianting VMs, a better alternative may be to create a separate module that you use to store the cached companies. I have a data module with its own internal cache that I use generically for this purpose, but you could create one specifically for companies, and store that information in it. It could even be responsible for loading its own data, if that's appropriate.
Here's some simple code to help explain:
Note this uses the sugar syntax for require JS - if you're using the array-based syntax, you'll need to translate accordingly. I can help if needed.
//companies-cache.js
define(function(require){
//some auto-loading logic here, if you wish
var companies = ko.observableArray([]);
return {
companies: companies
};
});
//vmCompanies, vmEmployees
define(function(require){
var companiesCache = require("viewModels/companies-cache");
//additional properties for this specific VM
...
return {
companies: companiesCache.companies
};
});

Related

Can.Model: creating model instances from plain objects

I have a can.Model with defined findOne, findAll methods. Sometimes however it is required to create model instances from plain objects, e.g. these objects are bootstrapped in html as globals during initial page load.
The problem is that these instances are not merged with instances stored in can.Model.store object. Moreover they are not getting stored there while they have defined id attribute. Is it expected behaviour? What is the right pattern to create model instances bootstrapped in html as variables?
Only models that are bound to (e.g. having data displayed on the page) will be added to the store. The store is only used for keeping those models. If nobody is listening to model changes there is no need to keep them stored (in fact, it would create a memory leak).
You can verify it like this:
var model = new MyModel({ name: 'David' });
model.bind('change', function() {});

Linq Projection is not including all data from the original entity

I have encountered a LINQ issue and hope that you can help me to figure it out.
Here is what is happening.
I get an IQueryable<LicenseEntity> of entities from the repository.
I look at the fields in these entities and see that they contain valid data. There is a field for a related entity Customer in the LicenseEntity. It contains valid data, too, because I loaded it with the LicenseEntity.
I use .Select to project each LicenseEntity to a LicenseViewModel.
For each LicenseEntity, a LicenseEntity is passed into AutoMapper.Mapper.Map and is loaded into a LicenceViewModel entity.
After all of the entities have been processed, when I look at the list of LicenseViewModels in the debugger, it reports a null reference exception and there are no items to view.
To determine whether AutoMapper what causing my problem, I replaced it with a MapMe(). When I stopped at the return statement in MapMe and looked at the s parameter, which is the original entity, I found that the data in it is okay except that the customer field is now null. I assume that Select has done something that I don't know about.
How I can make Select retain all of the information in the original entity when it is doing its projection? Our solution cannot materialize the list because it may be very, very large. I've included my test code below and would really appreciate your help.
// Get the IQueryable<LicenseEntity> list of licenses from the repository.
var list = LicenseRepository.List();
// Convert the IQueryable<LicenseEntity> to an IQueryable<LicenseViewModel>
var vmlist = list.Select(x => MapMe(x, new LicenseViewModel()));
//var vmlist = list.Select(x => AutoMapper.Mapper.Map(x, new LicenseViewModel()));
// This function was used to see the LicenseEntity that was passing into Map().
// I discovered that the entity has all the correct data except for a related
// entity field, which was present in the original LicenseEntity before
public LicenseViewModel MapMe(LicenseEntity s, LicenseViewModel d)
{
return d;
}
The following code works properly however it materializes the entities, which we cannot do.
List<LicenseViewModel> vms = new List<LicenseViewModel>();
foreach (var item in list)
{
var vm = AutoMapper.Mapper.Map(item, new LicenseViewModel());
vms.Add(vm);
}
You've tagged this LINQ-to-Entities but there's no mention of the underlying technology in the text. But it's very likely that the problem is caused by lazy loading of associated objects.
This is a design choice that applies to most ORMs that I've worked with. When you load an object, connected objects are not loaded by default. If they were loaded by default it's quite clear you'd quickly break everything
when you load a Licence, the related Customer is automatically loaded
when the Customer is loaded all related objects are loaded - Company, Address, all other Licences, etc
for each of those objects, every related object is loaded...
The answer is that you need to specify which related objects to load. In the Entity Framework you do this using the Include method.
Because you are using a repository abstraction you might find this more difficult than it needs to be, but without knowing more I can't give any advice. This type of functionality - pretty basic stuff - is always a difficulty with repositories and 'unit-of-work' patterns.
I think your mapping should be more like:
var vms = Mapper.Map<List<LicenseEntity>, List<LicenseViewModel>>(list);
(ie - you don't need the foreach loop).
But unfortunately I doubt very much that that'll fix your issue as I suspect that'll also materialize your entities.
I've found the solution for projecting domain entities to viewmodels. If you are struggling with the same kind of issue as I had, please review the following links:
http://lostechies.com/jimmybogard/2011/02/09/autoprojecting-linq-queries/
http://www.devtrends.co.uk/blog/stop-using-automapper-in-your-data-access-code
By the way, in one one my domain entities, I had a partial class with some "calculated" properties... Properties whose values were generated from other fields in the database record. These cannot be in the domain entity because they will interfere with the aforementioned solutions. I moved them to my ViewModel class, which is where they were really required, and all is well.
Hope that this info helps...
Mike

Generating Navigation for different user types, MVC, PHP

I have this idea of generating an array of user-links that will depend on user-roles.
The user can be a student or an admin.
What I have in mind is use a foreach loop to generate a list of links that is only available for certain users.
My problem is, I created a helper class called Navigation, but I am so certain that I MUST NOT hard-code the links in there, instead I want that helper class to just read an object sent from somewhere, and then will return the desired navigation array to a page.
Follow up questions, where do you think should i keep the links that will only be available for students, for admins. Should i just keep them in a text-file?
or if it is possible to create a controller that passes an array of links, for example
a method in nav_controller class -> studentLinks(){} that will send an array of links to the helper class, the the helper class will then send it to the view..
Sorry if I'm quite crazy at explaining. Do you have any related resources?
From your description it seems that you are building some education-related system. It would make sense to create implementation in such way, that you can later expand the project. Seems reasonable to expect addition of "lectors" as a role later.
Then again .. I am not sure how extensive your knowledge about MVC design pattern is.
That said, in this situation I would consider two ways to solve this:
View requests current user's status from model layer and, based on the response, requests additional data. Then view uses either admin or user templates and creates the response.
You can either hardcode the specific navigation items in the templates, from which you build the response, or the lit of available navigation items can be a part of the additional information that you requested from model layer.
The downside for this method is, that every time you need, when you need to add another group, you will have to rewrite some (if not all) view classes.
Wrap the structures from model layer in a containment object (the basis of implementation available in this post), which would let you restrict, what data is returned.
When using this approach, the views aways request all the available information from model layer, but some of it will return null, in which case the template would not be applied. To implement this, the list of available navigation items would have to be provided by model layer.
P.S. As you might have noticed from this description, view is not a template and model is not a class.
It really depends on what you're already using and the scale of your project. If you're using a db - stick it there. If you're using xml/json/yaml/whatever - store it in a file with corresponding format. If you have neither - hardcode it. What I mean - avoid using multiple technologies to store data. Also, if the links won't be updated frequently and the users won't be able to customize them I'd hardcode them. There's no point in creating something very complex for the sake of dynamics if the app will be mostly static.
Note that this question doesn't quite fit in stackoverflow. programmers.stackexchange.com would probably be a better fit

Lost in a simple MVC case - how do I return multiple Users?

I'm not sure which title would be more descriptive, so I kept it this way. I feel kinda lost in the world of MVC.
FYI: I use PHP, but that doesn't seem of much importance in this particular case.
My problem is as follows:
I have a UserController containing the following methods:
login
new
show
overview
Then I have my UserModel, containing - in this case - roughly the same methods:
login
create
fetch
The problem is: what do I keep my user data in once fetched from the database (or XML feed, or webservice, or whatever...)? I thought of a User 'business object', containing all (relevant) properties from the database. Then, when fetching the users from the database, I instantiate a new User object for each user I fetch. If only 1 user returned from the search, I return only the User object. If more users get returned, I instantiate a UserCollection object containing all User objects - in which case I can iterate over them, etcetera.
Is that a correct way of dealing with users in MVC?
And then: imagine I made an overview of 10 users. 5 of them get edited at once - imagine a status modification using checkboxes. How do I process the changes? Do I loop over all changed User objects and store them back in the database? Then it would start to look like an implementation of the Active Record Pattern, something I'm told not to use.
I hope someone can clarify which classes and/or methods I'd need to solve this 'architectural' problem.
Since it is a rather lengthy discussion. I will give the link to an article that I have written on MVC, trying to explain it in simple terms. You may want to take a look at it.
What is MVC pattern about?
If I understand correctly, your UserModel is a bit off;
the Model part of MVC is intended as a programmatic representation of the real world model.
Meaning- it represents all the properties and actions of the real-world subject. The classic example is the Car class, which has properties such as Wheel, CurrentSpeed, and actions such as GoForward(), GoReverse() etc..
So, in your case, I think your model should be what you described as a 'user business object'.
Your controller would be responsible for fetching the UserModels from storage (or wherever), and updating them back.
your workflow would be something like this:
View would call the Controller's GetUsers.
Controller goes to storage, and fetches a list of UserModels.
Controller returns them to the view.
View displays them in some way.
And the other way around for updating.
The UserModel class would be responsible for logic that pertains to individual users (for example- ChangePassword()).

Updating a many-to-many relationship

I've asked the question a few different times in a few different ways and I haven't yet gotten any responses. I'm trying again because I feel like my solution is too convoluted and I must be missing something simpler to do.
Using EF 4.1, POCO, DbContext API, AutoMapper, and Razor in an MVC 3 application.
I have a many-to-many relationship between two of my entities: Proposals and CategoryTags. I can successfully map (Automapper) a Proposal to my ProposalViewModel including the collection of CategoryTags.
In my View, I use javascript to allow the user to add, update, and remove tags by dynamically creating elements, each one that stores the ID of the chosen tag.
I can successfully post my ViewModel back to my controller with it's CategoryTags collection populated (although only with the ID property for each CategoryTag).
When that ViewModel is posted back to my controller, I don't know how to get those tags from the ViewModel and add them to my Model in such a way that db.SaveChanges() updates the database properly.
The only way I've had any success is to disconnect the CategoryTags collection in mapping (by namig them differently), iterate through each tag and manually look it up in my context and then call the .add() method. This is sloppy for a number of reasons which leads me to believe I'm doing it wrong.
Can anyone offer any direction at all?
UPDATE:
For anyone who is interested, my functional code:
Dim p As New Proposal
Dim tempTag As CategoryTag
p = AutoMapper.Mapper.Map(Of ProposalViewModel, Proposal)(pvm)
db.Proposals.Attach(p)
db.Entry(p).Collection("CategoryTags").Load()
For Each ct In pvm.Tags
tempTag = db.CategoryTags.Find(ct.Id)
If tempTag Is Nothing Then
Continue For
End If
If ct.Tag = "removeMe" Then
p.CategoryTags.Remove(tempTag)
Continue For
End If
p.CategoryTags.Add(tempTag)
Next
db.Entry(p).State = EntityState.Modified
db.SaveChanges()
Return RedirectToAction("Index")
The only working way is doing this manually - you can read full description of the problem if you want. The description is related to ObjectContext API but DbContext API is just wrapper suffering same issues (actually DbContext API suffers even more issues in this scenario and because of that I will skip solution with manually setting relationships).
In short. Once you post your data back to the controller you must create new context instance and attach your Proposal and realated CategoryTags. But after that you must inform the context about changes you did. It means you must say context which tags have been added to proposal and which have been removed. Otherwise context cannot process your changes because it doesn't do any automatic merge with data in database.
The easiest way to solve this is loading current Proposal with related CategoryTags from database (= you will have attached instances) and merge incoming data into attached object graph. It means you will manually remove and add tags based on posted values.

Resources