Hieararchical data in MVC - asp.net-mvc-2

In webforms I used a repeater inside a repeater(Hieararchical model). How can I achieve the same in MVC?
Example:
Data1 Data2
subdata1 subddata3
subdata2 subdata4
Data3 data4
subdata5 subdata7
subdata6 subdata8
I also require a two column layout as shown above. Any ideas ??

I can't remember where I read this, but it applies to you:
-- But won't we nead at least a repeater control in MVC?
-- We have a repeater control. It's called a for each loop
Let's say your view model has a property called Data of type IEnumerable<SuperDuper>. To iterate over it, you'd just do
<% foreach (var sd in Model.Data) { %>
<!-- write out fancy stuff -->
<% } <%>
To iterate over subdata, let's say that SuperDuper has a property named SubData that's also an IEnumerable<Something>. Nothing stops you from doing
<% foreach (var sd in Model.Data) { %>
<!-- write out some fancy stuff -->
<% foreach (var sub in sd.SubData) { %>
<!-- write out some more fancy stuff -->
<% }
} %>
For the two-column layout, resort to CSS.
And since Razor is on it's way, I can't resist to show you what those examples would look like with the new engine:
#foreach (var sd in Model.Data) {
<!-- write out fancy stuff -->
}
#foreach (var sd in Model.Data) {
<!-- write out some fancy stuff -->
#foreach (var sub in sd.SubData) {
<!-- write out some more fancy stuff -->
}
}

And with spark view engine you do this:
<div each="var item in Model.Data">
${item.Title}
<div each="var subItem in item.SubData" style="padding-left: 20px">
${subItem.Title}
<!-- Do some fancy stuff -->
</div>
</div>
Razor? Yuck! ;)

Related

knockout.js nested foreach on nested array

I've been looking at examples of nested foreach loops in knockout all afternoon, and I haven't been able to get anything working. My current setup, at least the relevant parts, are below.
ViewModel:
var sample = {
this.pageData = ko.observable();
this.panels = ko.observableArray();
};
ko.utils.extend(sample.prototype, {
activate: {
this.pageData(sampleData);
this.panels([
{
name: 'column1',
keys: ['key1', 'key2', 'key3'],
loadedWidgets: ko.observableArray()
},
{
name: 'column2',
keys: ['key4', 'key5'],
loadedWidgets: ko.observableArray()
},
{
name: 'column3',
keys: ['key6'],
loadedWidgets: ko.observableArray()
}
]);
this.loadWidgetPanels(this.panels(), this.pageData());
},
loadWidgetPanels: function (panels, pageData) {
for (var i = 0; i < panels.length; i++) {
var screens = filterContentByKey(pageData.Content, panels[i].keys);
if (screens) {
panels[i].loadedWidgets.push(widgetFactory.getWidgets(screens));
}
}
}
}
View:
<div>
<!-- ko foreach: panels -->
<div class="3columnStyle">
<!-- ko foreach: loadedWidgets -->
<!--ko compose: $data --><!-- /ko -->
<!-- /ko -->
</div>
<!-- /ko -->
</div>
I've confirmed that I'm getting back the right data in the right format in my loadedWidgets, but they don't display in the view. I can tell that the view at least knows how much data is there, because my DOM has a ko compose element for each widget. For example, the first column has a handful of widgets, and that div gets created with a handful of widgets in it. Column 2 has 2 widgets, and it gets 2 compose elements. Column 3 has 1 widget and gets one element. It's just not displaying the widgets themselves. Do I need additional elements or additional binding somewhere?
I have a working model of this that doesn't rely on nested loops. Instead of using the array of objects, it just creates each of the observable arrays. In other words, it's not looping. Instead of one array containing three objects, each with its own array, I have three arrays:
this.column1Widgets();
this.column2Widgets();
this.column3Widgets();
They're constructed the same way, just manually instead of looping. And the View looks more like this:
<div class="3columnStyle">
<!-- ko foreach: column3Widgets -->
<!-- ko compose: $data --><!-- /ko -->
<!-- /ko -->
</div>
<div class="3columnStyle">
<!-- ko foreach: column3Widgets -->
<!-- ko compose: $data --><!-- /ko -->
<!-- /ko -->
</div>
<div class="3columnStyle">
<!-- ko foreach: column3Widgets -->
<!-- ko compose: $data --><!-- /ko -->
<!-- /ko -->
</div>
I'm not sure why it's not working with the nested loop, but since the data is identical, I'm sure there's something I'm missing in setting up the View.
Without seeing the rest of your code, it's difficult to tell for sure. I'm a little suspicious of the sample object in your viewmodel. But it seems to me that you're not actually nesting your foreach's.
In your view, replace
foreach: loadedWidgets
with
foreach: $data.loadedWidgets
You need to reference the parent foreach in some way. $data represents the current item in the parent foreach, and that item, if I understand your model correctly, contains a loadedWidgets key.
Also, there's no need for containerless binding in your case.
As Eric Taylor suggested, it must have something to do with the containerless binding. I created a jsfiddle with some oversimplified object models, but changing my DOM from the above to the following immediately fixed the issue:
<div>Test</div>
<div data-bind="foreach: panels">
<ul data-bind="foreach: loadedWidgets">
<li data-bind="text: $data"></li>
</ul>
</div>
I don't think I have a good grasp of how the containers interact with the binding yet.

Dynamically add a form_tag using jQuery in Rails 3.2

My app has a user dashboard that lists rules a user has created. Each rule can be added to a "ruleset". For each rule, I want to display a button that when clicked, loads a form that allows the user to add that rule to a ruleset.
I have the following code in my view to show each rule:
<% #rules.each do |rule| %>
<div class="rule-wrapper">
<div class="rule-content row">
<div class="span6">
<strong>Rule:</strong> <%= rule.description %>
</div>
<div class="span2">
<div class="add-to-ruleset-form">
</div>
</div>
</div>
......
The pertinent part of my Rules controller is as follows:
class RulesController < ApplicationController
before_filter :authenticate_user!
def add_rule_to_ruleset
#ruleset = Ruleset.find(params[:ruleset_id])
#rule = Rule.find(params[:rule_id])
#ruleset.rule << #rule
end
....
Lastly, here is what my dashboard.js.erb file looks like:
$('.add-to-ruleset').click(function(e){
e.preventDefault();
$('.add-to-ruleset-form').html('<%= escape_javascript(link_to render :partial => "add_to_ruleset_form", :locals => {:rule => rule}) %>')
});
I'd like that when a button ('add-to-ruleset') is clicked, that the partial is rendered. I've tried using "escape_javascript" helper in my js.erb file but it isn't passing the "rule" variable corrent. I've also tried to use jQuery's load(), but I don't know how to pass the "rule" instance variable to the partial using jQuery. Is there another way?

KnockOutJS - Multiple ViewModels in a single View

I'm thinking that my application is getting quite large now, too large to handle each View with a single ViewModel.
So I'm wondering how difficult it would be to create multiple ViewModels and load them all into a single View. With a note that I also need to be able to pass X ViewModel data into Y ViewModel data so the individual ViewModels need to be able to communicate with each other or at least be aware of each other.
For instance I have a <select> drop down, that select drop down has a selected state which allows me to pass the ID of the selected item in the <select> to another Ajax call in a separate ViewModel....
Any points on dealing with numerous ViewModels in a single View appreciated :)
Knockout now supports multiple model binding. The ko.applyBindings() method takes an optional parameter - the element and its descendants to which the binding will be activated.
For example:
ko.applyBindings(myViewModel, document.getElementById('someElementId'))
This restricts the activation to the element with ID someElementId and its descendants.
See documentation for more details.
If they all need to be on the same page, one easy way to do this is to have a master view model containing an array (or property list) of the other view models.
masterVM = {
vmA : new VmA(),
vmB : new VmB(),
vmC : new VmC(),
}
Then your masterVM can have other properties if needed, for the page itself. Communication between the view models would not be difficult in this situation as you could relay through the masterVM, or you could use the $parent / $root in bindings, or some other custom options.
This is my answer after completing very large project with lots of ViewModels in single view.
Html View
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<div id="container1">
<ul>
<li >Container1 item</li>
<!-- ko foreach: myItems -->
<li>Item <span data-bind="text: $data"></span></li>
<!-- /ko -->
</ul>
</div>
<div id="container2">
<ul>
<li >Container2 item</li>
<!-- ko foreach: myItems -->
<li>Item <span data-bind="text: $data"></span></li>
<!-- /ko -->
</ul>
</div>
<script src="js/jquery-1.11.1.js"></script>
<script src="js/knockout-3.0.0.js"></script>
<script src="js/DataFunction.js"></script>
<script src="js/Container1ViewModel.js"></script>
<script src="js/Container2ViewModel.js"></script>
</body>
</html>
For this view I'm creating 2 view models for id=container1 and id=container2 in two separate javascript files.
Container1ViewModel.js
function Container1ViewModel()
{
var self = this;
self.myItems = ko.observableArray();
self.myItems.push("ABC");
self.myItems.push("CDE");
}
Container2ViewModel.js
function Container2ViewModel() {
var self = this;
self.myItems = ko.observableArray();
self.myItems.push("XYZ");
self.myItems.push("PQR");
}
Then after these 2 viewmodels are registering as separate viewmodels in DataFunction.js
var container1VM;
var container2VM;
$(document).ready(function() {
if ($.isEmptyObject(container1VM)) {
container1VM = new Container1ViewModel();
ko.applyBindings(container1VM, document.getElementById("container1"));
}
if ($.isEmptyObject(container2VM)) {
container2VM = new Container2ViewModel();
ko.applyBindings(container2VM, document.getElementById("container2"));
}
});
Like this you can add any number of viewmodels for separate divs. But make sure do not create separate view model for a div inside registered div.
Check MultiModels plugin for Knockout JS - https://github.com/sergun/Knockout-MultiModels
We use components to achieve that. (http://knockoutjs.com/documentation/component-overview.html)
For example, we have this component library we are developing: https://github.com/EDMdesigner/knobjs
If you dig into the code, you will see that for example we reuse the knob-button component in several places.

Creating a div element on the server side using C#

I am trying to create divs dynamically in a view page of an asp.net mvc project. This is the pseudo code:
<%
foreach (element in Model)
{
create the html div element with Div.id = Model.id
}
%>
I looked in the system.web.mvc.htmlhelper object. It provides support for a lot of html elements but not a div. Any Hints ?
There is no such helper for div. But, you can create your own HTML helper for it.
or simply you can go ahead and create divs in view page as
<%
foreach (element in Model)
{
%>
<div id="<%:element.id%>">
.. some html..
</div>
<%}
%>

How to use "this" and not "this" selectors in jQuery

I have 4 divs with content like below:
<div class="prodNav-Info-Panel">content</div>
<div class="prodNav-Usage-Panel">content</div>
<div class="prodNav-Guarantee-Panel">content</div>
<div class="prodNav-FAQ-Panel">content</div>
And a navigation list like this:
<div id="nav">
<ul id="navigation">
<li><a class="prodNav-Info" ></a></li>
<li><a class="prodNav-Usage" ></a></li>
<li><a class="prodNav-Guarantee"></a></li>
<li><a class="prodNav-FAQ" ></a></li>
</ul>
</div>
When the page is first displayed I show all the content by executing this:
$('div.prodNav-Usage-Panel').fadeIn('slow');
$('div.prodNav-Guarantee-Panel').fadeIn('slow');
$('div.prodNav-FAQ-Panel').fadeIn('slow');
$('div.prodNav-Info-Panel').fadeIn('slow');
Now, when you click the navigation list item it reveals the clicked content and hides the others, like this:
$('.prodNav-Info').click( function() {
$('div.prodNav-Info-Panel').fadeIn('slow');
$('div.prodNav-Usage-Panel').fadeOut('slow');
$('div.prodNav-Guarantee-Panel').fadeOut('slow');
$('div.prodNav-FAQ-Panel').fadeOut('slow');
});
So what I have is 4 separate functions because I do not know which content is currently displayed. I know this is inefficient and can be done with a couple of lines of code. It seems like there is a way of saying: when this is clicked, hide the rest.
Can I do this with something like $(this) and $(not this)?
Thanks,
Erik
In your particular case you maybe able to use the .sibilings() method something like this:
$(this).fadeIn().sibilings().fadeOut()
Otherwise, lets say that you have a set of elements stored somewhere that points to all of your elements:
// contains 5 elements:
var $hiders = $(".prodNavPanel");
// somewhere later:
$hiders.not("#someElement").fadeOut();
$("#someElement").fadeIn();
Also, I would suggest changing the classes for your <div> and <a> to something more like:
<div class="prodNavPanel" id="panel-Info">content</div>
....
<a class="prodNavLink" href="#panel-Info">info</a>
This gives you a few advantages over your HTML. First: the links will have useful hrefs. Second: You can easily select all your <div>/<a> tags. Then you can do this with jQuery:
$(function() {
var $panels = $(".prodNavPanel");
$(".prodNavLink").click(function() {
var m = this.href.match(/(#panel.*)$/);
if (m) {
var panelId = m[1];
$panels.not(panelId).fadeOut();
$(panelId).fadeIn();
return false; // prevents browser from "moving" the page
}
});
});