Angular bundled/minified by MVC fails to load main module - javascript

I've got an MVC 5 application that bundles up my angular app like such:
var angular = new ScriptBundle("~/App")
.Include("~/App/app.js")
.IncludeDirectory("~/App", "*.js", true);
bundles.Add(angular);
And then renders it in the head, after all other resources load, here:
(jquery, angular.min.js, other resources)
#Scripts.Render("~/App")
Open toggling my web.config's debug attribute to false:
<compilation debug="false" targetFramework="4.5.2" />
The main module for my angular app will not load:
Error: [$injector:nomod] Module 'wdf' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
Before you link me to documentation about how to ensure angular will load in minified form, know that I've already triple checked every single factory, controller, service [you name it], for the proper syntax outlined in AngularJS's documentation. I've also reduced my entire angular application down to a single file and a single line to eliminate possible other injection errors:
(function () {
'using strict';
var app = angular.module('wdf', []);
})();
I've also always had the app stated as such in HTML:
<!DOCTYPE html>
<html ng-app="wdf" ng-strict-di>
...
The application runs completely fine un-minified, even with ng-strict-di . I've been wrestling with this for hours and hours. Every thread I can dig up essentially tells me to ensure I've declared all of the module's dependencies right, but I'm pretty sure I have considering:
ng-strict-di hasn't caught anything I should have coded better
I've triple checked every friggin' file to ensure I've followed best practice for minification
And finally, the app doesn't even work it's most simplest form

Son of a.... of course, right I post, I find the issue.
I had failed to look at the developer tools to see if the site was properly loading the minified file. It was returning 403 for the bundled request. The solution outlined here with a style bundle worked the same.
MVC4 style bundle giving 403
In summary, the fix was this change:
FROM var angular = new ScriptBundle("~/App")
TO var angular = new ScriptBundle("~/wdf")
and then changing it the HTML as well: #Scripts.Render("~/wdf")
Hopefully this helps someone else who runs into the issue...

Related

Whats the best way to manage JavaScript with Ruby on Rails 6?

I'm giving a try to RoR 6 (I'm coming from MEAN and I've not touched RoR from version 3) and I'm finding some troubles to find the best way to manage JavaScript code. Maybe because of my background.
I've read a lot about the topic (including official guides https://guides.rubyonrails.org/working_with_javascript_in_rails.html) but It seems official documentation is out of date.
According to documentation, when you generate a controller from cli it should create a .js file for that controller but it doesn't occur. Besides, right now Webpack has been added to RoR 6 and JavaScript is no longer managed by Asset Pipeline (Am I right?) but there's not any reference to this matter.
I want to find a way to write code JS for every view and keep that code isolated from the rest.
Where should I put all the JS code?
How can I get isolation for the JS code of every view?
I've added jQuery to the project due to Bootstrap (by using Yarn) and to Webpack this way but $ or jQuery is undefined:
const { environment } = require('#rails/webpacker')
const webpack = require("webpack")
environment.plugins.append("Provide", new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
Popper: ['popper.js', 'default']
}))
module.exports = environment
I'd appreciate some help.
Thanks!
SOLUTION:
I found what I was looking for --> https://stimulusjs.org
Stimulusjs, created by Basecamp, adds a JS layer to every HTML view and let us to keep order and clarity when writing JS code. It connects the JS file with the DOM and nothing more. Enough to add some JS to improve functionality.
It pairs perfectly with Turbolinks and is ready to be used with Webpack. Besides, it can be learned in 10 minutes (no more). Installation is also absurdly easy.
Anyway, if you need to get some knowledge about RoR and Webpack/Webpacker, you can visit these links:
https://webpack.js.org/guides/getting-started/
https://github.com/rails/webpacker
https://medium.com/statuscode/introducing-webpacker-7136d66cddfb
And finally, if you don't wanna use a JS framework like Stimulus for managing JS code under RoR, you can always try these gems for specific page JS:
Paloma gem: https://github.com/gnclmorais/paloma (not checked)
Pagescript gem: https://github.com/maxcal/pagescript (not checked)
The change to Webpack is very new and the documentation has not quite caught up.
Generating asset files when running the generator was only done with the old assets pipeline and even then was a not really good idea. It relied on Sprockets special require_tree directive that would slurp up all the files in the directory and add them to the manifest. In alphabetical order, so you had no control over the order of execution.
It also fooled beginners into thinking that the js they put into users.js was only executed in their users controller when in fact it was all just slurped up into a single manifest.
With Webpack you explicitly import assets.
Where should I put all the JS code?
You're encouraged to place your actual application logic in a relevant structure within app/javascript.
How can I get isolation for the JS code of every view?
While you can use javascript_pack_tag in the view itself to require specific files this is not really a good idiom as it creates unnecessary HTTP requests and logic that is hard to follow.
If you want to ensure that code is executed when a particular view loads you can add data attributes to the body tag and create special events:
# app/layouts/application.html.erb
<body data-action="<%= action_name >" data-controller="<%= controller_name %>">
// fired when turbolinks changes pages.
$(document).on('turbolinks:load', ()=>{
let data = $(body).data();
// replace myns with whatever you want
$(this).trigger(`myns:${data.controller}`, data)
.trigger(`myns:${data.controller}#${data.action}`, data)
.trigger(`myns:#${data.action}`, data)
});
Then you can wrap functionality that should happen when a special page loads by listening for your custom events.
$(document).on('myns:users#show', ()=>{
console.log("We are on users#show");
});

how do I install an SDK using require, when I need to require an folder and this seems impossible?

PROBLEM DESCRIPTION.
I'm trying to follow https://github.com/lucascosta/facebook-js-ads-sdk to install the Javascript SDK for Facebook. Before anyone objects, I am absolutely aware of the compact version (https://developers.facebook.com/docs/javascript/quickstart), but that API is absolutely useless to me. I need the one produced by Lucas Costa, in order to be able to make changes to my ads account, which the recommended script by Facebook does not enable.
As usual installation instructions (see github.com/lucascosta/facebook-js-ads-sdk) are horribly predicated on mysterious conditions, that are unbeknown to me. I cannot get the SDK to work and would like someone to tell me explicitly what to do. The crux of the problem is the paradoxical situation: I am supposed to use a require('...') command (where??) to include the SDK (where is this supposed to be saved??) and yet require cannot be used on folders, but on scripts. Maybe somewhere there is a require.config file, I'm supposed to set up, but I have no idea, as, like I said, the instructions completely bypass any mention of the necessary starting conditions.
Here is what I have done so far. My folder-structure looks like this (I don't even know if this is right, as no-one explains it anywhere!):
[Serverroot]
— [folder with my website]
– facebook-ads-sdk (the folder one gets by downloading)
– css — pagestyles.css
– js — lib
require.js
— app
( some header scripts )
– img
( some images )
index.php
In index.php I have a block of html followed by some javascript. It is here, that I try to insert the setup / example code from . The browser cannot even get past the line const adsSdk = require('facebook-ads-sdk');. I have tried a number of things: require('./facebook-ads-sdk');, moving all this to the folder ./js/app in a script main.js and then writing in my html in index.php where main and require are located. Setting up a require.config (or requirejs.config) replacing require by requirejs, etc. and including the appropriate scripts in the <head> part of index.php. Nothing helps. Here are the errors: first with const adsSdk = require('facebook-ads-sdk'); I get
Error: Module name "facebook-ads-sdk" has not been loaded yet for context: _. Use require([])
Okay. How do I ‘load the Module for the context _’?? Reading requirejs.org/docs/start.html is of no help here. I tried require([], function() {require('facebook-ads-sdk')}); Same error. I tried require(['facebook-ads-sdk']);
I tried using the following commands in my script in index.php:
require.config({
shim: {
'facebook': {
exports: 'adsSdk',
},
},
paths: {
'sdk': './facebook-ads-sdk',
}
});
var adsSdk = require(['sdk']);
Then I get the error
Failed to load resource: http:// .... /facebook-ads-sdk.js the server responded with a status of 404 (Not Found)
Ie, the browser thinks I'm trying to include a script facebook-ads-sdk.js, but I’m supposed to(???) ‘require’ the entire folder! What is going on? Can someone please give me thorough instructions about the necessary folder structure and command, in order to get this SDK working?
Why is this so infuriatingly vaguely described online? Is there some kind of mysterious way of installing SDKs, about which ‘everyone’ knows, but never mentions?
UPDATE: SOLUTION. For any future google-searches for this same problem: via the following simple methods, one can install & embed the Javascript-FB-Ads-SDK:
install via npm install --save facebook-ads-sdk a copy of the npm modul Lucas Costa’s facebook-ads SDK.
Copy this folder to your website’s architecture (actually you possibly just need one subfolder / file).
in your HTML include the script (via <script type='text/javascript' src='...'></script>) with the src pointing to the file in the facebook-ads-sdk folder: /dist/iife.js.
In your page’s script, you now have access to then API via the global variable fb.
alternatively to 3:
3’. in your HTML headers make sure to include the require.js script via a <script>-tag. Then in your website’s javascript apply the following commands anywhere:
require.config({
paths: {
'sdk': './[FOLDERNAME OF SDK]/dist/iife',
}
});
require(['sdk']);
Credit and special thanks goes to #SLaks and #KhauriMcClain (I would give you up-points if I could) for this solution.
The instructions are assuming that you're using a bundling system like Webpack or Browserify.
If you are, the instructions "just work"; you can require() it and everything will work fine.
If you're building a non-trivial codebase, you really should use such a system.
If you aren't, you can reference iife.js in a <script> tag and that will create global variables.
You need to change the way you are using require.
You cannot use the synchronous require method to load dependencies if they have not been loaded before. Hence const adsSdk = require('facebook-ads-sdk'); will not work. You will need to use the form require(['name-of-script'], callback).
Require does not allow you to load an entire folder. You have to find the exact script you are trying to work with so var adsSdk = require(['sdk']); will not work.
Ultimately your code should look something like:
require(['some-facebook-script'], function(someFacebookScript) {
// Do some work here
});
The parameter passed to the callback function will be the loaded module you are trying to consume. When using the asynchronous version (as I just demonstrated) the return from require is not useful.

Using Angular Dragula without RequireJS

I would love to implement Drag and Drop in my Angular project using the angular-dragula module (https://github.com/bevacqua/angular-dragula). However, it seems to be heavily dependent on RequireJS. I've not used Require for a while and only then for an example app or two. Is there an easy way to untangle Require from this module?
The author seems to think it is simple (https://github.com/bevacqua/angular-dragula/issues/23) and has shut down similar questions as well without a real explanation. I've looked at the code and don't see how to load the module without adding RequireJS to my project (which I don't want to do). Am I stuck with either not using this module or adding Require or is there a way to use this without Require?
OK, after help from those who commented (thanks everyone!), I was able to get this to work. There are a couple things that you need to do. First, I was bundling this module with the rest of my modules and trying to call it. That will not work because it needs to initialize with a parameter (angular). Therefore, you need to do the following:
Add a reference to angular-dragula.js (or the min version) to your index.html page below the declaration for angular but above where you create your app.
When you declare the dependencies for your app, specify angularDragula(angular) (not in quotes).
Use dragula as you normally would. If you need to access the service, the name would be angularDragula.
For example, here is my declaration of app:
var app = angular.module('app', [
'ngRoute',
angularDragula(angular)
]);
And then to get a simple list to be drag and drop capable, this is my html:
<div dragula='"bag-one"' dragula-model="vm.items">
<div ng-repeat="item in vm.items">{{ item }}</div>
</div>
Note that I do not declare angularDragula anywhere, unlike the examples. In the example the author gives, he requires angular and creates the angular variable and then he requires angular-dragula and creates the angularDragula variable. This is not needed if you are not using RequireJS as long as you load the scripts in the right order.

Angular gives blank page when i use ui.bootstrap in my controller

I am trying to use the the angular bootstrap when i try to add the dependency in my controller and start the server with grunt serve i get a blank page.Please have a look at the bower components in the screen shot.
When i try to use it in angular.module('kbv1App', ['ui.bootstrap']).controller it fails if i remove the [ui.bootstrap] it works fine.
Any idea wha will be the issue?
Update. Console error
[$injector:modulerr] Failed to instantiate module ui.bootstrap due to: [$injector:nomod] Module 'ui.bootstrap' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument. http://errors.angularjs.org/1.2.18/$injector/nomod?p0=ui.bootstrap minErr/<#http://localhost:9000/bower_components/angular/angular.js:78:5 setupModuleLoader/</module/<#http://localhost:9000/bower_components/angular/angular.js:1645:1 ensure#http://localhost:9000/bower_components/angular/angular.js:1567:5 module#http://localhost:9000/bower_components/angular/angular.js:1641:7 createInjector/loadModules/<#http://localhost:9000/bower_components/angular/angular.js:3817:11 forEach#http://localhost:9000/bower_components/angular/angular.js:320:7 loadModules#http://localhost:9000/bower_components/angular/angular.js:3811:5 createInjector/loadModules/<#http://localhost:9000/bower_components/angular/angular.js:3818:11 forEach#http://localhost:9000/bower_components/angular/angular.js:320:7 loadModules#http://localhost:9000/bower_components/angular/angular.js:3811:5 createInjector#http://localhost:9000/bower_components/angular/angular.js:3751:3 bootstrap/doBootstrap#http://localhost:9000/bower_components/angular/angular.js:1410:1 bootstrap#http://localhost:9000/bower_components/angular/angular.js:1425:5 angularInit#http://localhost:9000/bower_components/angular/angular.js:1338:5 #http://localhost:9000/bower_components/angular/angular.js:21713:5 jQuery.Callbacks/fire#http://localhost:9000/bower_components/jquery/dist/jquery.js:3119:1 jQuery.Callbacks/self.fireWith#http://localhost:9000/bower_components/jquery/dist/jquery.js:3231:7 .ready#http://localhost:9000/bower_components/jquery/dist/jquery.js:3443:3 completed#http://localhost:9000/bower_components/jquery/dist/jquery.js:3474:3
The reason people are using angular-ui-bootstrap is to avoid using jquery and bootstrap.js (which relies on jquery) and opt for an Angular solution. So my first attempt at getting this app working is to
Remove jquery.js, jquery-ui.js, and bootstrap.js out of the page.
Start from just plain Angular + Angular UI Bootstrap first. Try some examples from http://angular-ui.github.io/bootstrap/ if you want to get familiar with the directive usage.
Then, if you really need to, just add jquery back in later. But from my experience, you won't really need to if you stick to Angular way of coding. If you come from a strong jquery background and just start coding on Angular, I recommend you read this: https://stackoverflow.com/a/15012542/3788115
The component `angular-bootstrap` should have two javascript files:
bower_components/angular-bootstrap/ui-bootstrap.js
bower_components/angular-bootstrap/ui-bootstrap-tpls.js
You only include the templates.
Edit: Corrected in the comments.
Use Chrome Canary for easier debugging, you will see the full error message instead of Uncaught object.
add ['ui.bootstrap'] on the main app.js

Debugging Unknown provider in minified angular javascript

I'm having a hard time trying to pinpoint which, of the very many, methods I have in my angular app that would be causing the error:
Uncaught Error: [$injector:unpr] Unknown provider: nProvider <- n
This only happens once the javascript has been bundled & minified by ASP.Net.
I have ensured that all the controllers, and any other DI, is using the minification-safe method, I.E My controllers/service etc are using the method:
appControllers.controller('myCtrl', ['$scope', function($scope){
//......
}]);
I've gone through every JS file in our app - there are a lot... and can't find anything that violates this way of injecting dependencies - though there must be one somewhere...
Is there a better way to pinpoint which method could be causing this error?
Thanks
For anyone else struggling with this problem, I found an easier solution. If you pull open your developer console (on chrome) and add a breakpoint where angular throws the error:
Then, on the stack trace on the right, click on the first "invoke" you see. This will take you to the invoke function, where the first parameter is the function angular is trying to inject:
I then did a search through my code for a function that looked like that one (in this case grep "\.onload" -R public), and found 8 places to check.
For anybody reading this, using Angular 1.3
You can now use Angular's ng-strict-di check like this:
<div ng-app="some-angular-app" ng-strict-di>
...
</div>
This will give you an appropriate error message if you didn't load your dependencies using the array syntax.
I had the same problem and I found a solution that could be helpful for the rest. What I propose is basically what I saw in the comments and docs. If you are using Angular 1.3.0 or above, you can use this:
<html ng-app="myApp" ng-strict-di>
<body>
I can add: {{ 1 + 2 }}.
<script src="angular.js"></script>
</body>
</html>
In my case, I have everything within a file app.js so the only thing I need to do for finding my DI problems is to add this little code at the end:
angular.bootstrap(document, ['myApp'], {
strictDi: true
});
It's better documented in Angular Docs
I hope that helps. Good luck!
As mentioned in the comments, These are the steps I took to try and find my JS error.
If there is another, easier, solution, please feel free to post it and I may mark it as accepted.
Trying to debug minified code is a nightmare.
What I eventually did was copy my minified javascript, directly from the inspector in Chrome.
I then pasted the JS into http://www.jspretty.com/ - I had tried http://jsbeautifier.org/ but found their site froze with such large JS code.
Once it was 'pretty-fied' I created a test.js file in my solution and pasted the, now easier to read code, into it.
Quick step to comment out the #script tag in my _layout and add a link to the test.js file and I was ready to debug a now, far easier to read, chunk of Javascript.
It is still pretty awkward to traverse the call stack, though now you can see actual methods it makes it far less impossible.
Something that helped me solve this (finally!) was actually already in the angular docs! If you add the ng-strict-di attribute to your code wherever you define your ng-app, angular will throw a strict warning so you can more easily see what's going on in development mode. I wish that was the default!
See the argument list at the bottom of the ngApp docs.
https://docs.angularjs.org/api/ng/directive/ngApp
The way this works for me is the following:
1) have two test specification html files (unit test) minimized and plain
2) make sure the bundling of the files are in the same order as the plain spec file (JS script reference)
3) make sure to explicitly declare all dependencies (array or $inject declares see http://www.ozkary.com/2015/11/angularjs-minimized-file-unknown-provider.html)
When there is a mistake on the unit test (miminized reference) file, I can compare and make sure the reference of the files is in the correct order as the working file.
hope that help.
I had the similar issue and used lots of time to investigate and figured out it was the Chrome extension Batarang that was injecting the bad code and error in Angular looked exactly the same. It's really a pity it's so hard to find what exactly is causing the problem.
I had the similiar issue too. The solution is exacly the answer from ozkary point 3, that is to make sure to explicitly declare all dependencies including "resolve" part of your route.
Below is my code.
when('/contact/:id', {
controller: 'contactCtrl',
templateUrl: 'assets/partials/contact.html',
resolve: {
contact: ['ContactService', '$route', function(ContactService, $route) {
return ContactService.getContactDetail($route.current.params.id);
}]
}
})
For those who's bootstrapping their angularjs app.
angular.bootstrap(body, ['app'], { strictDi: true });
Don't forget to debug in non minified code and you should be able to figure out pretty quickly the malformed dependency injection.
The malformed injection is usually formatted like this :
...
.run([ function(ServiceInjected){
...
But should look more like this
...
.run(['ServiceInjected', function(ServiceInjected){
...
This is tested in angularjs 1.7.2

Categories

Resources