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();
});
Related
Currently building a web page in Vue, and have hit a bit of an issue parsing and then rendering the <slot>'s child components.
I need to be able to take the slot, parse the components into an array, and then render those components for the end-user.
What I've Tried
I've tried many variations of things, most starting with this: this.$slots.default
This is the last version I tried
let slotComponents = [];
this.$slots.default.forEach(vNode => {
slotComponents.push(vNode);
});
But I've also tried selecting the elements within the vNode and using things like $childeren to select the components. No luck so far.
Potential Issues
The cause could be any number of things, but here is what I thought was going on (in order)
I'm not getting the components into the array properly
I'm not rendering them properly or missed something about how they render
Vue isn't supposed to do this?
Edit - Context
Seems like it would be easier if I gave you the full context of my specific problem.
Goal
To create a dynamic tab component. Should look like this.
// Example of component use
<tab-container>
<tab>
<!-- Tab Content -->
</tab>
<tab>
<!-- Tab Content -->
</tab>
<tab>
<!-- Tab Content -->
</tab>
<trash>
<!-- This one won't show up -->
</trash>
</tab-container>
In order to parse through this content, I needed to get the slot data out.
// Inside the <tabs-container> component
computed: {
tabs: function() {
let tabs = []
this.$slots.default.forEach(vNode => {
tabs.push(vNode);
});
return tabs;
}
}
// Inside the <tabs-container> template
<div>
{{tabs[currentTab]}}
</div>
You shouldn't be using template and computed properties if you want to programmatically render out <tab> inside <tab-container>. {{}} in templates are designed to perform basic operations of JS. The most appropriate way will be to use render function.
Render functions - Vue docs
Here is a working example that takes in few tabs components and shows only active tab component: https://jsfiddle.net/ajitid/eywraw8t/403667/
I have 8 jade view that only one of them is loaded at the time and is filled by jquery into a div which has a controller.
Now, I have 2 question about these:
Does it necessary to define again the controller on top of my partial view(same controller with main controller) ?
All of these views has same ng-click. but after loading they doesn't work. However they work by jquery click event. Should I do any extra thing with them?
I had same problem with li element before, but I resolve it by getting help from ng-click not working from dynamically generated HTML by using compileData but I can't get result with button.
Code:
Main jade:
div(ng-controller="elementCtrl")
div#ddd(class="col-lg-7 col-md-5 col-sm-7")
Partial view sample:
div#spPartial()
div.col-lg-12.col-md-12.col-sm-12
span.col-lg-2.col-md-5.col-sm-5 Name
input#EnglishName(name="name" type="text" ng-model="elementModel.Name" value="#{Name}" class="col-lg-5 col-md-7 col-sm-5")
button(type="button" compile-Data name="btnSaveElement" ng-click="saveElement()") Save
Main part of controller:
//It loads the partial view - It works successfully
$http.post('/api/elements/getElementTypesPartial',
{
"ElementId": elementId,
"ProgramId": newVal,
"ElementTypeId": elementTypeId
})
.success(function (d2) {
$("#ddd").html(d2);
}
//It doesn't work at all
$scope.saveElement = function () {
alert();
alert($scope.elementId);
}
And one additional thing is that I put $scope.saveElement in root of controller scope. I don't have any idea about how angularJs manage $scope, So I see $scope.elementId in client code. Is it right or I should regenerate it($scope.saveElement) every time that partial is loaded?
Sorry I couldn't find any reference which describes these...
You should get rid of the jQuery loading and use an angular router which will load templates based on route configuration.
Since they are loaded by angular, it does all the compiling for you.
The router takes care of the ajax to get the templates automatically also.
Controllers also get defined in the routing config so you would be able to remove ng-controller from the templates
The change over shouldn't take long since setting up routing config is fairly easy to get it started
This would clear up the ng-click problems
I am using bootstrap for this implementation. So I followed the guidlines in creating a top-down menu from bootstrap docs . This top-down menu should contain a room List. The menu is called 'Rooms'. My Jade and Javascript files are below(just the implementation of the top-down menu)
editor.jade
extends layout
block header
script(src='/javascripts/editor.js')
block content
h1(id="title") Editor
h2 Editor: #{editorName}
.dropdown
button#dropdownMenu1.btn.dropdown-toggle.sr-only(type='button', data-toggle='dropdown')
| Rooms
span.caret
ul.dropdown-menu(role='menu', aria-labelledby='dropdownMenu1')
#rooms
...
editor.js(javascript)
...
$.getJSON("/getContents", {theRoom: "roomList"}, function(room) {
room.activeRooms.forEach(function(theExit){
$('#rooms').replaceWith('<li id="rooms" role="presentation">' + theExit+'</li>')
});
});
...
Unfortunately, there is no top-down menu displayed on the screen. Can you please look at my code and tell me what I did wrong?
PS If my question is not clear, can you please tell me so that I can elaborate?
Make sure that the http request is filled and the callback is called. Then, replace your jquery part with something like this:
rooms.forEach(function(room){
$('.dropdown-menu').append('<li role="presentation">' +room+' trolol</li>')
});
What you do now is replace the #rooms elements with the first iteration of forEach, then it doesn't exist anymore and jquery can't find it.
http://jsfiddle.net/t6N9R/3/
(I converted the jade to HTML using one of the online converters)
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 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.