I have a very strange requirement that I need to bundle everything together in one HTML page with my Durandal Single Page application. I can make this away with my dependencies as I am defining them with a name:
define("models.mapper", [], function() {
});
However, it seems like it will not be possible to bundle durandal stuff as it defines modules without names:
define(['require', 'jquery'], function(require, $) {
// ....
}
This is fine when you want to make it work with path references but it seems like this will make it hard to inline this into HTML. Any ideas or suggestions on this?
Require.JS requires you to have only one anonymous define per file so that it can use the file path+name relative to the base path to give it a name. If you would like to have the durandal source inline on your page as well then you'll need to update their define lines to give them the appropriate names (i.e. define('durnadal/system', ......).
An easier approach may be to just build your source code in the structure of a normal durandal project and then use the RequireJS optimizer (http://requirejs.org/docs/optimization.html) to build them into a single JS file - if you configure this correctly without minification then you can just paste the file contents into a script tag on your page and it'll still be legible!
If you really wanted to you could then just continue developing in the single HTML file however you really should look at automating all of this into a grunt workflow and it shouldn't be too hard and you'll have much easier to manage code. Note that you may even be able to use the durandal grunt task to do this, but I'm not sure what options it allows you to provide but you can definitely use the requirejs grunt task and build it into your workflow without minification. With some templating task you could then inject that output into your final HTML page.
Related
I have a vue.js/Phoenix app. I'm trying to understand how to properly configure the frontend assets. I'm having trouble understanding why my priv/static/js/app.js file keeps updating whenever I Change something in other files. I'm trying to research this behavior but I can't seem to find out any information.
app.html.eex
<body>
<%= render #view_module, #view_template, assigns %>
<script src="<%= static_path(#conn, "/js/app.js") %>"></script>
</body>
My basic question is how to structure a vue.js app? The fact that I change something in asset/src dynamically changes something in static/js/app.js seems really strange. Does anybody have resources or answers on what might be happening here or places I can go to learn more?
In addition to what Pawel said, this behaviour might be intentionally configured. There is the watcher specified in config/dev.exs:
watchers: [
node: ["node_modules/brunch/bin/brunch", "watch", "--stdin",
cd: Path.expand("../assets", __DIR__)]]
That would be used in development mode to allow so-called “hot reload”: one does not need to reload the application when some changes in assets are made, app.js will be rebuilt and reloaded automagically.
There is also assets/brunch-config.js file, where one might specify rules of how the resulting app.js is being produced. By default is just compiles everything found in assets to the single javascript file, but this behaviour might be easily changed (e.g. one might exclude anything from being built into app.js and specify their own rules to provide an access to these excluded files.)
As contrary as this might sound, this is exactly the behaviour Phoenix (with Brunch) provides.
The main idea is to implement your JS functionality in assets/js/app.js, then Brunch (http://brunch.io/) as a build tool will take the content, compile/transpile and output to priv/static/js/app.js.
This means, with default configuration that comes with Phoenix, you can use ES6 in your code in assets/js/app.js, but this will be "translated" to executable form (that's understood by browsers), and located in priv/. priv/static is exposed publicly, and this will be the content available by:
<script src="<%= static_path(#conn, "/js/app.js") %>"></script>
To wrap up.
Code in priv/static is not meant to be changed by code, it gets there automatically by changes you put under your source control in assets/.
If that's any help, you can take a look at one of old blog posts about assets in Phoenix here.
Good luck!
I have been happy using webpack with Vue as of now. It uses a similar, configurable, watcher as the one mentioned by mudasobwa. In Webpack if you touch a file that is in part of the bundle it will recompile the needed files only (which can still be many depending on the dependency graph), probably brunch recompiles all.
I also use Yarn to manage npm, and I always include vuex unless it's really something just basic (although not related to file organisation it does help a lot organising vue on any non-trivial apps). Then
/assets
js
entry point files that I use for webpack output into its own individual bundles/apps
folders to organise these, usually /components-views-related, /store-related, /shared-utilities
css
.scss files, divided so that they can be split into "global" styles and individual styles that then are required in each "entry point". Then I use a "general" scss stylesheet on "all pages" and each page the corresponding css bundle where they're needed.
Then on the templates side, I wrote a small, overly complex, brittle, system to just automate the "bundle" that gets loaded on the template (in the html document head) but you can just load each bundle/s where you need them.
I have a multi-page / mulit-application minifcation I'm trying to make work.
the structure is as follows
common/
--main config file that defines the common libs
common/build
--app.build.js (see example below)
application1/
--application
--milion other files
application2/
--application
--thousand other files
this is placed in the header the page(s)
</style><script data-main='common/main' src='libraries/require.js'></script>
footer like this
<script>require(['../application1/application']);</script>
<script>require(['../application2/application']);</script>
This approach works perfect when using a bunch of files, and the applications run and interact perfect
However trying to make them into one seems a bit more of a challenge in terms of making it work.Granted I could just be an idiot
Build File
({
baseUrl: "../",
mainConfigFile: '../main.js',
optimize: 'uglify',
optimizeCss: 'standard',
out: "../global.min.js",
//insertRequire: ['main'],
include: ['../application1/application', '../application2/application'],
wrap: true // have tried both options - this makes the scenario at the bottom work
})
This works perfect and produces a lovely global.min.js. Challenge is it does not execute the callbacks on define calls I include it like so:
this is placed in the header the page(s)
</style><script data-main='common/main' src='libraries/require.js'></script>
footer like this
<script>require(['../common/global.min']);</script>
HOWEVER: if I keep the old requires in places like so:
<script>require(['../application1/application']);</script>
<script>require(['../application2/application']);</script>
it downloads 3 files
common/global.min.js
application1/application.js
application2/application.js
NOTE: At this point it is missing about 150 files, but the application(s) work perfect.
Having spent some serious time on this, my head is now properly wrecked and I can't figure out how to make it work from a single minified file.
Any help much appreciated as I have tried whatever I could think of. Happy to share all the files, howvere there is a lot of them ;-)
This is an educated guess, based on what you've shown in the question and experience with RequireJS' failure modes. I'm guessing "./common/global.min" is not a module name. (If you want to check this, open your optimized bundle and look at all the define calls. If you find that none of them has "./common/global.min", then it is not a module name.)
When you require "./common/global.min", RequireJS loads the corresponding file, and then looks in it for a module named "./common/global.min". It finds none, and your code does not get to run.
One way to fix this is to use a RequireJS configuration with your optimized bundle that maps names like this:
paths: {
'../application1/application': './common/global.min'
'../application2/application': './common/global.min'
}
And call require with these module names instead of "./common/global.min". This configuration tells RequireJS "when you are looking for these modules, look in that file: they are there" and indeed that's what the optimization process does.
I'm an experienced javascript programmer, and I'm trying to write my own modular game engine from scratch. I haven't used requirejs before, but after reading a bit about it, it sounds like it's probably the best way to manage all the components of the engine and load them into one coherent structure.
My main problem is that I'm not really sure how to really use requirejs. I've been looking over their API documentation, but I'm not sure how to integrate it with the way I've laid out my project.
Currently, my project uses the following structure:
src
engine.js //This contains the common engine stuff, most notably the engine namespace
resource
resource-module.js //This is the module constructor, which handles all the common stuff between the different
substructures
sprites.js //This is a substructure that contains sprite loading code
render
etc...
third-party
jquery
requirejs
I want to be able to load the modules independently of each other. It should be possible for instance to remove the audio module from the engine, and it still work. It should also be easy to substitute modules, or add new modules.
Also, I'm using jQuery for event handling, so it needs to be loaded before the engine is initiated.
You can find my current source here: https://github.com/superlinkx/lithium-engine
I know the current code is messy, and there isn't a whole lot built yet, but I'm mostly still figuring out how to best structure it. Any help/advice would be appreciated, but my main concern is how to structure it with requirejs so that it will be easier to compile into a single file for production use.
Require.js does not enforce any specific structure of your files. You can either specify the full paths in the require configuration, or just use the full path in the require() or define() calls. Both will work, however the latter will save you some typing and makes it easier to move stuff around when you include something from a lot of different places:
var $ = require("third-party/jquery");
require.config({
paths: {
"jquery": "third-party/jquery"
}
});
var $ = require("jquery");
I want to be able to load the modules independently of each other. It should be possible for instance to remove the audio module from the engine, and it still work. It should also be easy to substitute modules, or add new modules.
This is not something require.js does four you. You can decide when and when not to load it, but you would have to make sure that it won't break the rest of your code.
Also, I'm using jQuery for event handling, so it needs to be loaded before the engine is initiated.
You can do this in several different ways.
require() it in your main.js so that it is always loaded (you can also use the require-jquery.js, which has jQuery included).
Specify jQuery as a dependency of your engine
require.config({
paths: {
"jquery": "path.to.jquery",
"engine": "path.to.engine"
},
shim: {
"engine": {
"deps": ["jquery"]
}
}
});
Pass jQuery to the define() call in your module (probably the best choice)
define(["jquery"], function ($) {
// your engine code
}
Ok, I've read a lot of information about the new Asset Pipeline for Rails 3.1 and I couldn't find a proper answer to my doubt.
I was loading my .js files according to the view#action I was rendering, on demand. I was doing this to prevent incorrect bindings and to load small .js files.
candidate_opportunities#index
$(".sortable_drag_n_drop").sortable({
update: function(event, ui) {
$.post('/candidate_opportunities/sort', $(this).sortable('serialize'));
},
handle: 'span'
});
candidate_companies#index
$(".sortable_drag_n_drop").sortable({
update: function(event, ui) {
$.post('/candidate_companies/sort', $(this).sortable('serialize'));
},
handle: 'span'
});
$(".sortable_drag_n_drop").disableSelection();
What is the best solution now?
Should I change my bindings and let Sprockets compile all my .js files using //=
require_tree . ?
Or should I try to load my
.js according to my views, so I don't end up with a huge
application.js?
If you are updating this to the pipeline you have several options. You should probably take the way the pipeline should work into account in deciding.
Broadly speaking, the aim of he pipeline is to join all your JS into one file and minfy/compress it. The point in doing this is reduce the number of requests per page, and to allow far-future headers to be set so that the resource is cached at the browser or somewhere in a transparent proxy/cache.
On to the options.
1. The same as you do now.
You could keep doing the same thing as you do know. I presume that you are using the rails helpers to add these view JS files in the main layout file. You could keep doing the same with the pipeline, however you must add all the files you use to the precompile array:
config.assets.precompile += ['candidate_opportunities.js', 'candidate_companies']
The assets must be in assets/javascripts but there is no need to add them to the manifest file as you are adding each on individually.
It is highly recommended that you stick with the Rails defaults for the pipeline and precompile assets for production.
The downside is an extra request on those pages, but this is only an issue if the app is under high load.
2. The Asset Pipeline Way (TM)
To do this with the pipeline you would need to move these files into assets/javascripts and require_tree as you say.
The issue in your case is that the JS snippets target the same class (but with a different post URLs), so this is not going to work. And with require_tree the order of the files might not be what you want.
A new 3.1 app generates files for views (I think), but the expectation is that they will target unique attributes (from a site perspective) in the markup, because all the files get included in the application.js
To get around the problem of JS clashes. I would suggest that you refactor the JS snippet so that it is more generic. You could use a data-post-url attribute on the sortable object:
<ul class="sortable_drag_n_drop" data-post-url="/candidate_opportunities/sort">
and then collect the url in your JS.
Not only is that DRYer, but you have less overall JS and can fully use the pipeline as intended.
I'm frustrated on Rails asset pipeline. Maybe not the whole asset pipeline thing but the way Rails organize javascript is really not logical.
As of now, Rails has a separate javascript file per controller. Which is somewhat good in terms of logical organization. But then the asset pipeline compresses all those files into one big js file. So basically your js files are well organized but then they are loaded all at once, resulting to variable clashes, function clashes, other code clashes and other unexpected behavior. Because what we really want as developers is a simple way to execute or load a page specific javascript.
One famous approach is to just include a specific javascript file per page. Yes, that will work, but we are not using the performance boost given by the asset pipeline if we are requesting different javascript files per page.
My solution is to create an javascript object that holds all the page-specific functions then fetch and execute them once the matching controller-action pair is executed by Rails. Something like this:
PageJs = {};
PageJs["users/new"] = function(){
alert("I will be called when users/new action is executed");
};
Basically, that's the core idea. Well I've implemented that idea and created a gem for it. Checkout Paloma, and see how you can logically organize your js files and execute page-specific javascript without complex setups.
Here's an example on how Paloma is used:
Javascript file:
Paloma.callbacks['users/new'] = function(params){
// This will only run after executing users/new action
alert('Hello New Sexy User');
};
Rails controller:
def UsersController < ApplicationController
def new
#user = User.new
# No special function to call,
# the javascript callback will be executed automatically
end
end
That's just a quick example, but it has lot more to offer, and I'm sure it can solve your problem. Easily.
Thank You!
I'd like to split my views in Grails into 2 files a .gsp file and a .js file so that I get a cleaner Javascript separation from my views. So here's an example:
views/index.gsp
views/index.js
views/home/index.jsp
views/home/index.js
But when I simply add the index.js script reference like this:
<script src="index.js" type="text/javascript"></script>
all I get is a 404.
Does anyone knows how to deal with this?
A great benefit would be to have the ability to use view data inside the index.js file to produce the desired content.
Matthias.
Actually, it should be perfectly possible to serve a JS file (or any other file type) as a GSP from your grails-app/views/ directory. The only thing you have to do, is define a suitable URL mapping for those GSPs, e.g.:
"/javascript/home/index"(view:'/home/index.js')
With this URL mapping, you can put your JS code into grails-app/views/home/index.js.gsp (note the trailing .gsp) and you can use any grails tags in your JS source. To ensure that your JS is delivered with the correct content type, you may want to place
<%# page contentType="text/javascript"%>
at the beginning of your GSP.
Unfortunately, the createLink tag doesn't support link rewriting to views, but it should be easy to write your own tag to create those links.
Anyways, keep in mind that this won't have a very positive impact on your app's performance. It's usually better to have static JS files (and also serve them as static resources) while passing dynamic stuff as parameters to JS functions for example. This will also keep you from some headaches wrt. caching etc.
The idea is good, but Grails has this directory structure for a reason. The view folder is intended for a certain artifact type (views)..
You could clone your view folder structure under web-inf, but that gives you more work as I guess the idea behind this is to keep related files close together for convenience reasons.
Even though I'm not to excited about storing Javascript together with the view I loved Robert's idea of hooking into the build process by using build events to copy javascript sources into the right directory! If you decide to go down that road you might as well compress the sources while you're at it. ShrinkSafe is popular library.
I don't think you are allowed to access js inside views/
if you need to do that ... here is the trick
create your js and rename it with myjs.gsp (use "")
iniside _myjs.gsp type you js
... write down you js in here ...
inside you gsp (for example: index.gsp, view.gsp, etc)
type this tag to upload you js
Update 2:
Grails offer the possibility of hooking into the build lifecycle using custom events.
An event handler can be written which synchronises all JavaScript files under grails-app/views with the target folder of web-app/js.
Place the custom code in $PROJECT/scripts/Events.groovy. The PackagingEnd is a good target for the invocation, since it happens right after web.xml is generated.
eventPackagingEnd = { ->
// for each js file under grails-app/views move to web-app/js
}
Update
If you'd like the JavaScript files simply 'meshed' together, you can do that using symlinks, e.g.:
grails-app/views/view1/index.js -> webapp/js/view1/index.js
As far as I know, there is no way of forcing grails to directly serve content which is outside of web-app.
Alternatively, you can inline your JavaScript, but that can have performance implications.
JavaScript files belong under web-app/js.
Then you can reference them using <g:javascript src="index.js" />.