I'm writing an Angular application using Webpack.
I have a user profile JavaScript object that I'd like to template into the index page via an inline script, for performance, so that the client doesn't have make another request and delay the rest of the page loading.
Using requirejs you can name an inlined module so you can depend on it later. Is there any way of doing this webpack? or am I stuck with declaring it as a global?
As an example here's what you could do in Require JS;
<html>
<body>
...
<script>
define('userProfile', [
'angular'
], function(angular) {
return angular.module('userProfile', [])
.constant('userProfile', Object.freeze({
id: '$!{user.userid}',
name: '$!{user.fullname}',
userType: '$!{user.userType}'
}))
});
</script>
...
</body>
</html>
The user fields such as '$!{user.userid}' are templated in when the index page is served.
To depend on this in your app you could simply do something like
define([
'userProfile',
], function() {
return angular.module('my-app', [
'userProfile',
]);
});
You can specify a variable or module to be external on your webpack config so that you just include it on the page by a script tag. This is what i do to skip libraries bundle with my script
// webpack.config.js
"externals": {
"jquery": "jQuery",
"angular": "angular"
},
But as angular works you don't need to import the module with webpack
<script>
(function(angular) {
angular.module('userProfile', [])
.constant('userProfile', Object.freeze({
id: '$!{user.userid}',
name: '$!{user.fullname}',
userType: '$!{user.userType}'
}))
})(angular);
</script>
And in your module you can.
angular.module('whatever', ['userProfile'])
Now you can use the user profile constant anywhere in your angular application.
angular.module('whatever').controller(['userProfile', function (userProfile) {}]);
You just need to make sure that the userProfile script is executed before the your bundle
Related
fter including require tag the application is behaving abnormal way .is there any way i can bootstrap my application apart from below code .
main.js
require(['/module.js'], function() {
angular.element(document).ready(function () {
angular.bootstrap(document, ['myApp']);
});
});
When I written as single file js file the code is working properly.
module.js
var name = 'myApp';
angular.module(name, [])
.controller('Controller', require(['controller.js']))
.factory('Service', require(['service.js']))
.filter('Number', require(['filter.js']));
I have included my main.js in index.html . index html has 3 views i am displaying them based on ng-show from index.html.
The problem is module.js loading properly and js files too. Script is not executing properly so that my entire index.html page including 3 views displayed automatically with error messages.
Control is not going to controller.js/service.js
Error :
Error: Unknown provider: depsProvider <- deps .
Did i miss any define code? Thanks in advance
Angular does not support AMD by default, You need to config angular to export angular object. Please check out the this post for more details.
require.config({
paths: {
'angular': '../lib/angular/angular'
},
shim: {
'angular': {
exports: 'angular'
}
}
});
Your module.js should be defined with define method of requirejs and it should return module.
You can omit file extesion (.js) while using requireJs
I have two questions.
I am trying to learn RequireJS and use it along with ASP.NET MVC bundling & minification. I am using a separate config file for RequireJS which holds the bundling information. My first problem is how do I pass on the bundle path generated by MVC to the require.config.js file. A clean way to do that will be as below:
index.cshtml
<script id="requirescript" type="text/javascript" src="~/Scripts/require.config.js"
data-baseurl="#Url.Content("~/Scripts")"
data-bundlepath="#System.Web.Optimization.Scripts.Url("~/bundles/scripts").ToString()"></script>
require.config.js
var reqScript = document.getElementById('requirescript');
var baseUrl = reqScript.getAttribute('data-baseurl');
var bundlePath = reqScript.getAttribute('data-bundlepath');
var require = {
baseUrl: baseUrl,
bundles: {
bundlePath : ['jquery','jqueryui','mymodule']
}
}
};
When I do the above, RequireJS tries to load a non-existing script named bundlePath.js, instead what I want is to load the bundled script which is '/bundles/scripts?v=GZ0QWPB4G0soItEmlsPC6Yp3zftCRVleVTcH3LseMWo1' which contains my modules. So first, my question is how do I pass the bundle URL, as generated by the server, to RequireJS in the require.config.js file without hard-coding the bundle path?
Secondly, the jqueryui module seems to be not loading. I have added the module name in the AMD code in jquery ui min file. How do I make jquery ui work with RequireJS and ASP.NET bundling?
There is a NuGet package RequireJs.NET https://www.nuget.org/packages/RequireJsNet/ which is an implementation of RequireJs for .NET MVC.
RequireJS is an implementation of Asynchronous Module Definition (AMD) that provides all the tools you need to write modular JavaScript. If you are working on a large project with a lot of JavaScript code, many external components and frameworks, RequireJS will help you manage the complexity of dependencies.
You will have access to a configuration file (json) which will look like this:
{
"paths": {
"jquery": "jquery-1.10.2",
"jquery-validate": "jquery.validate",
"jquery-validate-unobtrusive": "jquery.validate.unobtrusive",
"bootstrap": "bootstrap",
"respond": "respond",
"i18n": "Components/RequireJS/i18n",
"text": "Components/RequireJS/text",
"menu-module" : "Controllers/Common/menu-module"
},
"shim": {
"jquery-validate": {
"deps": [ "jquery" ]
},
"jquery-validate-unobtrusive": {
"deps": [ "jquery", "jquery-validate" ]
},
"bootstrap": {
"deps": ["jquery"]
}
},
"autoBundles": {
"main-app": {
"outputPath": "Scripts/Bundles/",
"include": [
{
"directory": "Controllers/Root"
}
]
},
"require-plugins": {
"outputPath": "Scripts/Bundles/",
"include": [
{
"file": "Components/RequireJS/i18n"
},
{
"file": "Components/RequireJS/text"
}
]
}
}
}
And after that you will render RequireJs config into your layout.
#using RequireJsNet
<!DOCTYPE html>
<html>
<head>
<!-- head content -->
</head>
<body>
<!-- body content -->
#Html.RenderRequireJsSetup(new RequireRendererConfiguration
{
// the url from where require.js will be loaded
RequireJsUrl = Url.Content("~/Scripts/Components/RequireJS/require.js"),
// baseUrl to be passed to require.js, will be used when composing urls for scripts
BaseUrl = Url.Content("~/Scripts/"),
// a list of all the configuration files you want to load
ConfigurationFiles = new[] { "~/RequireJS.json" },
// root folder for your js controllers, will be used for composing paths to entrypoint
EntryPointRoot = "~/Scripts/",
// whether we should load overrides or not, used for autoBundles, disabled on debug mode
LoadOverrides = !HttpContext.Current.IsDebuggingEnabled,
// compute the value you want locale to have, used for i18n
LocaleSelector = html => System.Threading.Thread.CurrentThread.CurrentUICulture.Name.Split('-')[0],
// instance of IRequireJsLogger
Logger = null,
// extensability point for the config object
ProcessConfig = config => { },
// extensability point for the options object
ProcessOptions = options => { },
// value for urlArgs to be passed to require.js, used for versioning
UrlArgs = RenderHelper.RenderAppVersion()
})
</body>
</html>
For further reading you can access the documentation page: http://requirejsnet.veritech.io/ .
Or the github project (for issues and questions) : https://github.com/vtfuture/RequireJSDotNet
In this package exists a compressor too for bundling and minification (based on YUI compressor).
Instead of bundlePath use the bundle path "/Scripts/bundles/scripts" . it'll work.
An old question but you can use #Scripts.RenderFormat() to get MVC to output the filename of the bundle on it's own. e.g.
Bundle
bundles.Add(new ScriptBundle("~/bundles/bundleName").Include(
"~/Scripts/filename1.js",
"~/Scripts/filename2.js",
"~/Scripts/filename3.js"
));
View
<script type="javascript">
var arrayOfFiles = [#Scripts.RenderFormat("\"{0}\",","~/bundles/bundlename")];
</script>
This sets arrayOfFiles to
["/Scripts/filename1.js","/Scripts/filename2.js","/Scripts/filename3.js"]
or if the bundling is enabled you just get
["/bundles/bundleName?v=13232424"]
You can then pass this array to other javascript libraries.
There is something really weird going on when I'm using RequireJS with AngularJS. I managed to load all my angular dependencies through RequireJS. I can see those scripts downloaded when I open up the Sources pane in Chrome's developer tool. But Angular keeps throwing an error in the console that it failed to instantiate the module:
Uncaught Error: [$injector:modulerr] Failed to instantiate module MyTestApp due to:
Error: [$injector:nomod] Module 'MyTestApp' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure...<omitted>...0)
It seems like Angular, when loaded with RequireJS, cannot bind with the ng-app tag in the HTML page. I'm not sure if this is the case but it seems like so to me because when I import angular.min.js manually into the HTML page, it all works fine.
Did I do anything wrong when using RequireJS with Angular? How should I use the two together? Here's how my code look:
index.html
<!doctype html>
<html lang="en" ng-app="MyTestApp">
<head>
<meta charset="utf-8">
<title>AngularJS</title>
<link rel="stylesheet" href="css/style.css"/>
<script data-main="main" src="js/require.js"></script>
</head>
<body>
<div ng-controller="TestController">{{helloMessage}}</div>
</body>
</html>
main.js
require.config({
baseUrl: "scripts/app",
shim: {
"angular": {
exports: "angular"
},
"angular.route": {
deps: ["angular"]
},
"bootstrapper": {
deps: ["angular", "angular.route"]
},
},
paths: {
"angular": "../angular",
"angular.route": "../angular-route",
"bootstrapper": "bootstrapper"
}
});
require(["angular", "angular.route", "bootstrapper"],
(angular, ngRoute, bootstrapper) => {
bootstrapper.start();
}
);
bootstrapper.js
function run() {
app = angular.module("MyTestApp", ["ngRoute"]);
app.controller("TestController", TestController);
console.log(app); //Prints object to console correctly, ie, angular was loaded.
}
Here is how I would do it (DEMO).
In main.js, require angular, your app and maybe a controllers.js and other files:
require(['angular', 'app'], function (app) {
angular.element(document).ready(function () {
angular.bootstrap(document, ['MyTestApp']);
});
});
In app.js, require angular and angular route:
define(['angular', 'angular.route'], function() {
var app = angular.module("MyTestApp", ["ngRoute"]);
return app;
});
This is manual bootstraping and therefore does not need the ng-app tag at all.
I'm currently working on a pretty big application with angular and requirejs and I prefer to load the "big" libraries that are used by the whole app anyway independently from requirejs.
So I load one big file which includes angular, angular-route, requirejs, main.js in the beginning. Or if it makes sense to use a CDN version, load it from there. On the other hand I load every controller, directive, filter and service on request. I currently have 50+ controllers which allready makes a difference in initial load time.
But that all depends on the size of your app.
First you does not need to get a variable from the load of "angular.route". The module will be directly loaded in angular.
I think you should also wait for the dom ready event and also make a requirejs app module that will be in charge of loading all app dependencies:
app/app.js:
define([
"angular",
"angular-route",
"app/controllers",
"app/directives",
[...]
], function(angular){
var app = angular.module('app', [
"ngRoute",
"app.controllers",
"app.directives",
[...]
])
.config([function(){
// app configuration goes here
}]);
return app;
})
main.js
require(["angular", "app/app"],
function (angular, app){
angular.element(document).ready(function() {
angular.bootstrap(document, [app.name]);
});
}
);
I am using RequireJS for large apps to manage 3rd party dependencies. It handles dependencies on the fly and helps a great deal with dependency management and modularization.
Can Angular do something like this, or does it make sense to integrate it with RequireJS? I would like to lazy-load 3rd party or custom dependencies as needed (compiling options like RequireJS optimizer would be nice too). Any advise or experience would be appreciated!
I use RequireJS, it's pretty straight forward:
require: require.config({
paths: {
jquery: '/assets/js/jquery',
"jquery.bootstrap": '/assets/js/bootstrap.min',
angular: '/js/libs/angular/angular',
sanitize: '/js/libs/angular/angular-sanitize',
text: '/js/libs/require/text',
async: '/js/libs/async/async',
moment: '/assets/js/moment.min'
},
baseUrl: '/js',
shim: {
'angular': {'exports' : 'angular'},
'sanitize': {'exports' : 'sanitize', deps: ['angular']},
'jquery.bootstrap': {deps: ['jquery']},
},
priority: [
"angular"
]
});
require(['jquery', 'angular', 'app'], function($, angular, app){
$(document).ready(function () {
var $html = $('html');
angular.bootstrap($html, [app['name']]);
$html.addClass('ng-app');
});
});
Then inside a file called app (depended on by the first require statement) I have
define(['angular','sanitize'], function (angular, sanitize) {
'use strict';
return angular.module('myApp', ['ngSanitize']);
});
Using RequireJS with AngularJS makes sense but only if you understand how each of them works regarding dependency injection, as although both of them injects dependencies, they inject very different things.
AngularJS has its own dependency system that let you inject AngularJS modules to a newly created module in order to reuse implementations. Let's say you created a "first" module that implements an AngularJS filter "greet":
angular
.module('first', [])
.filter('greet', function() {
return function(name) {
return 'Hello, ' + name + '!';
}
});
And now let's say you want to use the "greet" filter in another module called "second" that implements a "goodbye" filter. You may do that injecting the "first" module to the "second" module:
angular
.module('second', ['first'])
.filter('goodbye', function() {
return function(name) {
return 'Good bye, ' + name + '!';
}
});
The thing is that in order to make this work correctly without RequireJS, you have to make sure that the "first" AngularJS module is loaded on the page before you create the "second" AngularJS module. Quoting documentation:
Depending on a module implies that required module needs to be loaded
before the requiring module is loaded.
In that sense, here is where RequireJS can help you as RequireJS provides a clean way to inject scripts to the page helping you organize script dependencies between each other.
Going back to the "first" and "second" AngularJS modules, here is how you can do it using RequireJS separating the modules on different files to leverage script dependencies loading:
// firstModule.js file
define(['angular'], function(angular) {
angular
.module('first', [])
.filter('greet', function() {
return function(name) {
return 'Hello, ' + name + '!';
}
});
});
// secondModule.js file
define(['angular', 'firstModule'], function(angular) {
angular
.module('second', ['first'])
.filter('goodbye', function() {
return function(name) {
return 'Good bye, ' + name + '!';
}
});
});
You can see that we are depending on "firstModule" file to be injected before the content of the RequireJS callback can be executed which needs "first" AngularJS module to be loaded to create "second" AngularJS module.
Side note: Injecting "angular" on the "firstModule" and "secondModule" files as dependency is required in order to use AngularJS inside the RequireJS callback function and it have to be configured on RequireJS config to map "angular" to the library code. You may have AngularJS loaded to the page in a traditional manner too (script tag) although defeats RequireJS benefits.
More details on having RequireJS support from AngularJS core from 2.0 version on my blog post.
Based on my blog post "Making sense of RequireJS with AngularJS", here is the link.
In my project I want to use RequireJS and bootstrap my app as follows:
requirejs.config({
baseUrl: 'scripts/vendor',
paths: {
jquery: [
'https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min',
'jquery'
],
angular: [
'http://ajax.googleapis.com/ajax/libs/angularjs/1.0.4/angular.min',
'angular'
],
app: '../app'
}
});
require(['require', 'jquery', 'angular', 'app'], function(require, $, angular, app) {
console.log(require);
console.log($);
console.log(angular);
console.log(app);
});
On my index.html only RequireJS is loaded via script tag, where the RequireJS loads the above code.
What works:
- in my Network monitor I can see that RequireJS, jQuery, Angular and app are loaded
- The console.log messages print correct for require, jQuery and app
The angular object is somehow undefined. But if I don't load it from CDN and use my local load, it works! The local file is a RequireJS wrapper that looks like this:
define(['/components/angular/angular.min.js'], function () {
return angular;
});
How do I get this work with Angular'S CDN? Or does this depend on support from Angular?
First, you are confusing "paths" with "shim"
Path is good, don't go for "shim" behavior. But, you need to make your "paths" proper:
paths: {
jquery: 'https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min',
// NOTE: angular is "plain JS" file
angular: 'http://ajax.googleapis.com/ajax/libs/angularjs/1.0.4/angular.min',
app: '../app'
}
Then, you need to let go of the need to have something returned to you... Just "use the force, Luke" :) and expect the right globals to be there when you need them:
require(['jquery', 'app', 'angular'], function($, app, thisValueDoesNotMatter) {
// you don't need to wrap "require" Just use global
console.log(require);
console.log($);
console.log(app);
// note, angular is loaded as "plain JavaScript" - not an AMD module.
// it's ok. It returns "undefined" but we just don't care about its return value
// just use global version of angular, which will be loaded by this time.
// because you mentioned it in your dependencies list.
console.log(window.angular);
});