I have a site set up in ASP.NET MVC3 right now, but the layout of everything is optimized for mobile devices.
I would now like to combine some of the existing views together to take advantage of the additional screen space in a desktop browser or tablet.
I have two views now, one displaying a list of links and a second displaying some content generated by each link; both these views are handled by separate controllers. Here is the list view (simplified). It uses a layout page, so this is what would be displayed when #RenderBody() is called in the layout (my second view also works like this, with the same layout file).
#model IEnumerable<CommandCenterEntity>
#{
if (Request.Browser.IsMobileDevice) {
Layout = "~/Views/Shared/_LayoutMobileContent.cshtml";
}
else {
Layout = "~/Views/Shared/_LayoutDesktop.cshtml";
}
}
<ul data-role="listview" id="commandcenterlist" data-filter="true" data-inset="true" data-theme="b">
#foreach (var entity in Model)
{
<li>
#Html.CommandCenterLinks(#entity, x => Url.Action("Index", "Worksheet", new { ParentId = #entity.ID_Item }))
</li>
}
</ul>
The CommandCenterLinks helper just generates an "a" tag containing a different icon depending on some properties of the entity.
I'd like to combine my views in such a way that when I click a link in the list, which would be in a div tag on the left of the screen, the generated content will display in a separate div tag on the right.
I was hoping I could reuse the same actions/controllers used in my mobile layout, so I tried using jQuery to intercept the click event on the list and capture the data returned from the action, and inject it into the div:
$(document).on("click", "#commandcenterlist a", function (e) {
$.mobile.showPageLoadingMsg();
$.ajax({
url: this.href,
success: function (data) {
$.mobile.hidePageLoadingMsg();
$("#desktopContentPane").html(data);
}
});
return false;
});
However, the data I get back in the ajax success handler contains the full html page,
<!DOCTYPE html>
<html>
<head>...</head>
<body>...</body>
</html>
...when all I want is the content that would have been generated in the layout's #RenderBody() call. I tried changing the controller method to have return PartialView("Index");, but it gave the same result. Is there a way for me to get back only that portion of the view? Or is there some cleaner, more "MVC" way of going about this?
The best option is really to return your partial view from a separate controller action. So, a few things I would change to make it work:
Take the content you want to share (whatever you want to display in the div for desktop) and put it in a partial view.
Create a controller action which returns just this partial view (use the PartialView method and pass in your partial view name), and call it from your javascript in the desktop version.
In view for the mobile version, you can use the RenderPartial method to include the contents from the partial view in your mobile page.
Related
I've read through a few posts here regarding this topic, but none seem to answer which method is the best for allowing sub-pages access to the host page's <script> section.
I load home.php, which has a menu displayed in one of its two <div> sections. The HTML for this menu is on home.php itself. The user is able to interact with buttons and dropdowns in the menu portion of home.php through scripts on home.php, like:
$("#graphsButton").click(function() {
if($(this).text()=="Graphs") {
$(this).html("<span><i class='fa fa-check-circle checked'></i> Graphs</span>");
graph = true;
} else {
$(this).html("<span>Graphs</span>");
graph = false;
}
});
After the user performs their initial operations, that larger menu is replaced by a smaller menu to provide the user more screen real-estate, using the following code:
function shrinkMenu() {
$('#search').html('');
$('#search').animate({
width: '4%'
}, 500);
$('#returnMain').animate({
width: '96%'
}, 500);
$('#search').load('smallMenu.php');
}
If the user wants the big menu back, on smallMenu.php I then have another <script> section with this code:
$('#growMenu').click(function () {
$('#search').html('');
$('#search').animate({
width: '20%'
}, 500);
$('#returnMain').animate({
width: '80%'
}, 500);
$('#search').load('largeMenu.php');
});
largeMenu.php contains a duplicate copy of the original HTML for the menu that loaded along with home.php, and visually, it looks exactly the same to the user.
As I toggle between large menu and small menu however, the <script> sections contained in home.php that pertain to the id tags in the original menu HTML that loaded with home.php no longer work.
In order to make it work, it seems that I would have to re-initialize all my plugins on each page load, and maintain 3 separate pages' <script> sections that are all duplicates of one another, and this seems very inefficient and probably not the best way to go about this.
Is there a better way for me to achieve the functionality I am trying to code here?
Update
My code looks like this at a high level:
<div class="searchParent" id="search">
<div class="return"></div>
<div class="menu">
<div class="largeMenu" id="largeMenu"></div>
</div>
</div>
What you are doing is fine by replacing the menu from a php file that contains a "partial view" which is your menu HTML. The partial view relies upon the parent page code, so you don't need to duplicate any code from the parent script into the partial view. The partial view can expect there is script code on the parent page it can be used with.
As I toggle between large menu and small menu however, the
sections contained in home.php that pertain to the id tags in the
original menu HTML that loaded with home.php no longer work.
The problem you are having though is that you need to ensure that the code on home.php that references these menus uses a parent container as its reference point instead of direct IDs. You are replacing elements (menus), so the bindings are lost when you do that unless you bind on a parent higher up the DOM that doesn't get replaced (remember that events bubble up).
From what I see in your code snippets, it looks like the menu is loaded into the #search container. So just ensure the code in home.php uses that container as the reference point, using something like on like this:
$('#search').on('click', '#graphsButton', function() {
// ...
});
My web app has a master jade template that contains all the main components that need to be loaded on EVERY page of the app. One of these components is a navbar which I've split out into another template. This navbar has a dropdown that needs to be populated dynamically from a DB.
The problem I'm having is that I want to populate the dropdown at the time that the navbar gets rendered. Because I'm using express 4.0 with nodeJS, I thought of using a helper function that would populate the list straight from within the navbar template. Because this function is async though, the navbar renders itself before the dropdown items are retrieved from the DB.
I also thought of using AJAX on the page load to populate it or even just passing the dropdown items into the template beforehand but I don't want to have to pass in this list on every single res.render() call in my controllers.
What is the best way to go about doing this?
Sample templates
master.jade:
doctype html
html(lang=en)
head
block title
body
#header
include navbar
block headerContent
#content
block content
navbar.jade:
... //nav code
li(class='dropdown')
a(href='#', class='dropdown-toggle', data-toggle='dropdown') List
span(class='caret')
ul(class='dropdown-menu', role='menu')
li
each val, index in list
a(href='#') val
A typical page in the system would just extend the master template and fill in the appropriate header and content blocks.
Thanks!
EDIT:
Adding a sample controller to show render call
sampleController.js:
...some error checking
model.save(function(err, results) {
if (err)
res.render('view', {error: 'some error'})
else
res.render('view', {data: *formatted results obj*})
The data specifically relates with the content that should be rendered on this particular view and not necessarily what's required by the master. I don't want to have to pass in the navbar items as well.
AJAX seems impractical for a navbar.
You can use a middlewear to populate locals object
app.use(function(req,res,next){
res.locals.list = [...];
// or
app.locals.list = [...];
next();
});
I am currently working on a web application that uses jquery 1.9.1 and jquery mobile 1.3.2 in visual studio(MVC4 template).I have many pages where i use a side panel.The side panel has almost same elements in it, so i decided to put it in a partial view,but the elements in the side panel are not exactly same in all the pages.
so i am thinking to put all the elements of side panel used in all the pages in a partial view and selectively render them according to the page, i am using.
so is there any way by which i can render only selective elements from a partial view?
Do you really need to do this using Javascript?.
Lets organize the ideas:
I have many pages where i use a side panel
What I would do here is to create a layout page that you could use on all the views where you require to add this side panel. So the views you want to render this layout on the final html output should include the following section:
#{
ViewBag.Title = "Roles";
Layout = "~/Views/Shared/_SidePanelLayout.cshtml";
}
so i am thinking to put all the elements of side panel used in all the pages in a partial view and selectively render
Ok I would do the same, however, the rendering of the options, I would not use javascript. I would prefer to send the html of the only required options by sending the models to the view. This is the process I would code:
1-Whenever you request a page that uses this side panel layout, you should fill a ViewBag property (or a model) to pass the page name. So lets say that you have your Home/Index view uses this layout, you can do something like this:
public ActionResult Index()
{
ViewBag.PageName = "Home-Index"; //you could use constants or enums as best practice
return View();
}
At some point on your _SidePanelLayout view, I would place a Render Action call. This render action will in fact, render the side panel view with the specific options for that page:
#{ Html.RenderAction("GetOptions", "SidePanelController", ViewBag.PageName); }
This means that you will require a SidePanelController class with the method that will return your SidePanelView (with the specific options for your the page that you requested):
public class SidePanelController: Controller
{
public ActionResult GetOptions(string pageName)
{
//you may want to change this List<string> for a list of objects that include
//the properties you need like url, name, tooltip, etc
List<string> menuOptions = new List<string>();
//Determine which options should be rendered
/* your code */
//return the view with the filtered options
return PartialView("_SidePanelView", menuOptions);
}
}
That way you would have your requirements fullfilled. It's a bit more complex than using javascript, but it's more robust solution.
Hope this helps.
I'm attempting to render JavaScript from a Razor Partial Page into the bottom of a (layout) Page. In Including JavaScript at bottom of page, from Partial Views Becuzz specifies that using a #section {} might be helpful for this purpose, but such sections are not rendered from Partial Pages.
One could in theory circumvent the problem by rendering the section of JavaScript outside of the Partial Page, into the Page itself. However, this is not possible, given that I want to reference a html element inside the script, as follows:
#section ScriptTag
{
<script type="text/javascript">
var example = $('##Html.FieldIdFor(m => m.ExampleProperty)').val();
});
</script>
}
#Html.TextBoxFor(m => m.ExampleProperty)
How can I make this work?
You can only call RenderSection between two Views/Layouts that are directly related.
In this situation you would need to essentially redefine and render the section in your View in the middle.
See: http://blogs.msdn.com/b/marcinon/archive/2010/12/15/razor-nested-layouts-and-redefined-sections.aspx for a clearer explanation
I'm trying to figure out what the best practice is for using jQuery in an MVC app. Specifically, I would like to know what I should do so that I don't clutter all my views with individual document.ready statements.
As an example:
I have the following Views:
/Views/Shared/_Layout.cshtml
/Views/Home/Index.cshtml
/Views/Home/_Dialog.cshtml
/Views/Home/_AnotherDialog.cshtml
I have a controller action that will render the Home/Index View, which uses the Layout and renders two partial views (or editor templates, display templates, etc.). This one controller action has rendered 4 or more views. Each view is using some jquery document.ready code.
Currently, I have the code at the bottom of each view:
// In Index
<script type="text/javascript">
$(function() {
$('#tabs').tabs()
});
</script>
// In _Dialog
<script type="text/javascript">
$(function() {
$('#some-dialog').dialog( ... );
});
</script>
I know this isn't a very good practice because it is already getting unmanageable in my small project. What are some good practices to follow when I have tons of pages that all need some jQuery / javascript initialization code separated across dozens of views?
You could do something along the lines of what Telerik do with their javascript registrar. Basically, make this registrar available in your view model. At the simplest level, all it has to do is keep track of strings added to it:
public class JavascriptRegistrar
{
private StringBuilder jsBuilder_ = new StringBuilder();
public Add(string js)
{
builder.Append(js).Append('\n');
}
public string ToString()
{
return "<script type=\"text/javascript\">" + jsBuilder_.ToString() + "\n</script>";
}
}
Your partial views will then add to this when rendering:
<h1>In my view!</h1>
#Model.Registrar.Add("$function() { /* ... */ }")
Finally, at the bottom of your main view, when you're done:
#Model.Registrar.ToString()
Which will write out all the javascript it has collected during rendering.
If the initialisation is specific to a view and you know it definitely won't be used outside that view, for example some page specific behaviour, then just leave it in the view!
There is nothing wrong with having script tags in all your views, as long as you aren't replicating js between views. I think people tend to misunderstand 'separation of concerns' in this case and think that simply means 'keep different languages away from each other at all costs'...that is wrong, clearly if some page initialisation logic/behaviour is specific to a page, then the html and js intrinsically 'concern' each other, therefore moving the js into a separate file is not really 'good practice', if anything it makes your code more difficult to understand.
I personally like to open up a View, and be able to see all the js and css that is specific to that page as soon as I open it, makes it nice and readable. However, obviously if code needs to be shared then you need to bust it out your view and get in your scripts folder whwere it can be referenced by anything!
EDIT
In your example above I see in your Index view you initialise your tabs. This is fine as it is, however, if you added tabs somewhere else in the project then it might be better to create your tabs using a .tabs class rather than #tabs id, and then in an external js file initialise all your tabs at once by calling $('.tabs').