I have an app that has a single page main.html, and a few major templates admin.html, user.html, etc. This app is not currently backbone, just some sections of it (slow migration).
In the admin page, I have BB views for users, etc. When a user selects a path to /admin/users, e.g. then a master template loader (non-BB) loads admin.html into a standard location in main.html, and then runs reset on the usersView:
// pseudocode
function() {
master.loadTemplate("admin.html").then(function(){usersView.reset();});
}
admin.html contains several elements, only one of which is the template for usersView. So it might look like:
<div id="admin-fragment">
<div id="admin-users">
<!-- template for usersView -->
</div>
<div id="admin-something else">
</div>
</div>
So when I load the script that contains usersView, it looks like:
UsersView = Backbone.View.extend({
el: "#admin-users"
});
All pretty good. Here is the issue.
Sometimes, a user might go somewhere else in this single page app, then go back to /admin/users. So the master template loader loads (and sometimes reloads, depending) the entire admin.html. But the usersView is already attached to the existing (now orphaned) #admin-users.
How do I resolve this? I see two ways, could use some ideas:
instead of usersView.reset() when I select /admin/users, create a new usersView:
// pseudocode
function() {
master.loadTemplate("admin.html").then(function(){usersView = new UsersView();});
}
My concern is the performance and management hit.
somehow tell the existing usersView to "reparent" / "reconnect", i.e. find the element anew.
Any better ideas?
See these posts on good patterns for rendering nested views in Backbone:
Assigning Backbone Subviews Made Even Cleaner
Rendering Views in Backbone.js Isn't Always Simple
Related
So I have started to learn Meteor and trying to get my head around how I should format the template correctly. This is how I set up my project:
A/view.html - html file for template A
A/script.js - import A/view.html. Potentially be the "controller" to work with the interaction
B/view.html - html file for template B
B/script.js - import A/view.html. Potentially be the "controller" to work with the interaction
routes.js - route file, include all script.js for template A and B
So I have two questions:
First, I want to make A as the base template, meaning it will have style and javascript tags as well as a "styles" and "scripts" optional blocks in case the child template wants to add extra files. How can I do this? I have tried normal ways:
Creating 2 blocks named styles and scripts in each child templates. This doesn't work since routes.js imports everything, meteor complains there are 2 templates having the same name
Using Template.dynamic. This work but I have to declare what template I want to render in the block of "styles" and "scripts", which is a bit untidy, in my opinion, when the project goes big.
The second question, as I described what I am currently doing with my routes, what is the best way to localize(?) the block to the current file?. Would it be possible to have 2 blocks called "scripts" in 2 different child templates and meteor not complaining?
Thanks guys :)
I'm not 100% sure what you mean by "blocks", but I assume you want to have A be the template that everything else fits into, and then have other pages feed into it?
If so, it sounds like you would want to use dynamic templates, and have A be a layout.
Here is an example of a layout, which imports other templates from one of my projects (It actually imports two constant templates (loginNavbar and modalWindow) as well as dynamic ones depending on what I call to it. You could add as many styles and other things as you want to the layout itself):
<template name="loginLayout">
<div class="loginNavbarTemplateDiv">
{{> loginNavbar}}
</div>
<div class="loginContentTemplateDiv">
{{> Template.dynamic template=content}}
</div>
{{> modalWindow}}
</template>
So then my routes.js looks like this (renders the outside loginLayout with the inner content of login or register):
FlowRouter.route('/login', {
name: 'login',
action: function() {
BlazeLayout.render("loginLayout", { content: 'login' });
}
});
FlowRouter.route('/register', {
name: 'register',
action: function() {
BlazeLayout.render("loginLayout", { content: 'register' });
}
});
Overall, I wouldn't have two templates named the same thing, and if you structure your files/app properly, it shouldn't be too untidy.
Please let me know if this helps and if you have any other questions, I'd be glad to help.
-
Here is a great tutorial on dynamic templates if you want more:
https://themeteorchef.com/tutorials/using-dynamic-templates
And a guide on how to structure your Meteor app files (helps a ton and makes everything better):
https://guide.meteor.com/structure.html
I'm working on a website where I have classic multiple pages powered by blade templates, but I want to use vuejs2 components for the most dynamic needs.
It's working fine, but I couldn't find a convenient way to declare containers parsed by VueJs for components.
I want to use many components in many places, but I can't declare a big main container as VueJs container because of conflicts with <script> tags.
If I do that I get errors like : - Templates should only be responsible for mapping the state to the UI. Avoid placing tags with side-effects in your templates, such as <script>, as they will not be parsed.
I currently do something like that in my app.js file :
new Vue({
el: '#need-components-1'
});
new Vue({
el: '#need-components-2'
});
new Vue({
el: '#need-components-3'
});
I really don't like it, I'd like to be able to declare either the whole content as VueJs-able, or at least use a common class or attribute for containers. Here I need to add a new id each time I want to use a new place for components. Moreover, VueJs posts console errors for every time an element is not found because obviously they are not all always loaded.
Is there any elegant / convenient way to do that ?
Thanks
Use some identifier that an element is a root Vue and then, when the page loads, iterate over the elements you find on the page and create Vue's for them.
Here is an example.
console.clear()
const vues = document.querySelectorAll("[data-vue]")
for (let v of vues)
new Vue({el: v, data: {message: "Hello from Vue"}})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<div data-vue="true">
<h2>{{message}}</h2>
</div>
<h4>Some other page content</h4>
<div data-vue="true">
<h2>{{message}}</h2>
</div>
<h4>Some other page content</h4>
<div data-vue="true">
<h2>{{message}}</h2>
</div>
Obviously you'll need to figure out a way to marry the appropriate data with the correct Vue if it should be different from Vue to Vue.
The title is a big ambigious, but this is the problem.
Assuming I am working in AngularJS and I have several views that largely need the same logic.
Code overview
app.js
$routeProvider
.when('/sheet/:sheetid/:screenid', {
templateUrl: 'views/sheet.html',
name: 'sheets',
controller: 'SheetCtrl'
});
index.html
<div class="container" ng-view></div>
The shared controller
Now this is the part where it all falls down in my mind. I need this controller to work on all 10 sheets. Right now I'm loading the data/model for the sheet through a service, which is working for now. The thing is though, that every sheetid shares a lot of code, but they can also differ from one another.
For instance:
sharedController.js
angular.module('App')
.controller('SheetCtrl', function ($scope, $routeParams, sheets) {
$scope.sheetid = $routeParams.sheetid;
/** GET THE SHEET */
//Gets the correct model from the sheets service (via promise)
$scope.sheetData = sheets.getSheet($scope.sheetId);
/** FACTSHEET SPECIFIC LOGIC */
//TODO: Figure out how to load specific factsheet controls in here
/* THE LOGIC FOR ALL THE SCREENS. WE NEED THIS AT ALL TIMES */
$scope.setScreen = function(index) {
...
};
//there are some more functions here that need to be shared
});
The view is a problem too
Every sheet has to be parsed in the same template. The problem is though that even these templates can differ tiny bits, and only from one div nested in this template. I would want to assign sheetID specific controller logic to the sheet-content, and also give the sheet-content it's own template/view.
<div class="sheet">
<div>
close
</div>
<div class="sheet-content">
<!-- Assume sheet specific controller code to target this div-->
</div>
</div>
Now what?
Not quite sure how to continue from here. It seems like I should be able to somehow dynamically assign controller logic for specific sheetID's to the sheet-content div and assign a template here. I'm really looking for the method to do this as clean and DRY as possible.
For instance, I could copy/paste the view and sharedController 10 times and assign them to every possible path, but I really do not want to repeat myself.
Another problem is that every factsheet should build his own model somehow. Right now I am doing it through a service with predefined data, but it would be cleanest if the specific controllers are handling that. The problem is when I put that logic in the specific controllers, the sharedControllers can't use that model anymore and won't build again.
If anyone could point me in the right direction I'd be very happy about that.
Partials loaded inside the parent inherit parent controller:
<div ng-controller="SheetCtrl" class="container" ng-view></div>
Here's what I eventually did. I included a view in the sheet-content according to a $scopevariable. I created 10 different views. In these 10 views I only changed the ng-controller element. All these views then ng-include another view which is shared across all 10 controllers.
I'm not totally happy about loading the data for the sheet in the sheet.js controller, but for now it seems the most DRY method of working. If I can think of a way to load this data in the corresponding sheet controller I'd rather do that.
sheet.js
This controls all of the sheets, contains shared functions and loads the data
$scope.template = {
url: '/views/sheets/sheet' + $scope.sheetid + '.html'
};
sheet.html
This view simply loads the template provided above, looking at the sheetid
<div ng-include="template.url">
sheet1.html (goes on to sheet10.html)
Simply assigns the right controller so I can seperate my code
<div ng-controller="Sheet1Ctrl">
<div ng-include="'views/sheets/sheet-wrapper.html'"></div>
</div><!--controller-->
sheet-wrapper.html
This view is shared across all sheets. Changing this view changes all the sheets
<div class="mpj-factsheet-dynamic"></div>
I'm working on a little home brew project and I've found I've been spoiled by working with RoR and more specifically RoR partials.
My project is entirely client side. So I'm using javascript and HTML5. Now what I would like to do is this: have a home screen template with a container in which I could do something like the
classic <%= yeild %> tag does in RoR.
I have thought about using iframes but that seems messy. I have also thought about using xmlHTTP requests to get the file in my javascript and then update the innerHTML of my content div with the file stream. I have done this before for personal projects, but it's hacky and I have to flag browsers like Chrome with a --allow-file-access-from-files tag. Which obviously I can't advise end users to do.
My other thought was to write the html as a javascript string, and then just put different strings as the value of content.innerHTML but this sounds stupid hard to maintain and just not clean at all.
Ultimately I am even up for writing my own solution (which I would then post here as the answer for anyone else looking) but I wanted to know if there was already a solution out there.
The ultimate end goal behavior would follow this flow:
Main_page:
<div id="main_content">
<!-- this is the yield area -->
</div>
App starts and the file menu.html is loaded into the yield area:
<div id="main_content">
<!-- this is the content of menu.html, notice it's like a partial,
there is no body or head or doc type tags, just a list -->
<ul>
<li>Menu item</li>
</ul>
<!-- this is the end of the menu.html content -->
</div>
And finally when they click on Menu item it loads the contents of menu_item.html into the content div replacing the current content (menu.html).
Some research I already did:
Div like an iframe?
Div src attribute plugin (looks interesting but runs on webserver)
Stack question about updating div with javascript
Found a link somewhere that led to Pure This looks like it could do the trick, but seems like it would be difficult to implement for a lot of content (since I don't have anything generating the json, I would have to do it all by hand.)
This talks about the seamless attribute of iframes, looks promising but only chrome has implemented and even then its bugs have been abandoned.See here for status of webkit development of the seamless attribute.
If you're using jQuery, you could use jQuery.load(): http://api.jquery.com/load/
Example:
$("#main_content").load("menu_item.html");
If you're worried about the safety of the <div> (which is a good thing to be worried about), then what you'd be better off doing is using an intermediary <div> which is not attached to the DOM.
function parse_html (html) {
var div = document.createElement("div");
div.innerHTML = html;
var scripts = div.getElementsByTagName("script"),
i = scripts.length;
while (i > 0) {
div.removeElement(scripts[i]);
i -= 1;
}
return div.firstChild;
}
Then if you grabbed an HTML file via XHR:
var xhr = new XMLHttpRequest();
xhr.open("GET", "/templates/menu.html", true);
You could put your var child_el = parse_html(xhr.responseText); in your XHR callback.
Then just append that element to your div.
That function is assuming that your partial has one root element (assuming that you could have multiple back-to-back partials, but each template would start from a single element.
If that's not the case, then you'd create a document fragment in the function (document.createDocumentFragment();) and append each of the elements in order, to the fragment, returning the fragment at the end of the function, which you would then append to the div (of course, this would make managing the nodes inside the div that much harder, after the fact).
I'm doing something similar to this for a few pet projects, using a simple template system built on {% %} for injecting data-properties ({% item.title %}) or arbitrary JS/return values from functions, within the html.
This is only a basic implementation, and there are lots of things you could do with this...
Supporting arbitrary nesting levels, for templates inside of templates is going to be more work.
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').