IntelliJ IDEA / Webstorm and AngularJS / Ionic - angularjs

I love it when multiple technologies come together to produce a doozy...
The following AngularJS template squawks an error in the IDE ("can't resolve file"). I find the inspection wildly convenient and I don't simply want to turn it off.
/my_project/www/templates/logo.html
...
<img src="img/logo.png"/> <<< file at /my_project/www/img/logo.png
...
Question:
How can we allow an IDE like IntelliJ IDEA or WebStorm to play nice with Ionic/AngularJS/Cordova in this situation?
NOTE: I cannot simply mark the www folder as a "resources root" and use absolute references because ionic needs relative refs...
Or does it? Is there a way to fix this on the cordova side of things to allow absolute refs? i.e., so it doesn't break when deploying to Android (because we need the prefix file://android_asset/www/)

Inspired by this answer, I ended up creating a recursive search/replace script in the build process. I made a cordova hook for "after_prepare" and used node with the replace package. Now I can use absolute refs and get the full benefit of the IDE... and then at build-time they get converted to relative.
Here is a sample hook file to get you started. Don't forget to add refs for css files or things like templateUrl in your app.js if you're using angular/ionic. And of course don't forget to modify the platform specifics to your needs.
#!/usr/bin/env node
var replace = require("replace");
var wwwDir = process.argv[2] + '\\platforms\\android\\assets\\www';
// convert all src and href tags to relative in index.html + components\*
replace({
regex: '(src|href)=([\'"])/+',
replacement: '$1=$2',
paths: [
wwwDir + '\\components',
wwwDir + '\\index.html'
],
recursive: true,
silent: false
});

Related

Sencha - Conditionally adding namesapce in requires

I have a requirement that I want to add namespaces in requires conditionally.
e.g. In below example I want to add 'views.popupgrid' name space on specific condition. Currently it's always loaded.
requires: ['Ext.window.MessageBox','views.popupgrid','user.MyUser' ]
Conditional dependencies are not supported by the Sencha toolchain. While you would be able to write in a text editor of your choice
requires:[
(location.hash=='#test')?'testpopup':'normalpopup'
]
and this would work in the uncompiled version, Sencha Cmd would not be able to compile it correctly, and would throw errors.
Therefore, Sencha Architect does not support this syntax.
What you can do, while staying Standards-compliant: you can use Ext.Loader.loadScript, e.g. like this:
Ext.define('MyForm',{
extend: 'Ext.form.Panel'
initComponent:function() {
var me = this;
me.callParent(arguments);
if(x==3) Ext.Loader.loadScript({
url:'MyCustomFormComponent.js',
onLoad:function(){
me.add({
xtype:'mycustomformcomponent'
});
})
});
}
})
Please note that in this case you will always have to deliver MyCustomFormComponent.js alongside the minified app.js, because the dependency cannot be resolved by the toolchain. Also, depending on the connection, there may be a visible delay before the resource is loaded and the component is added to the form.
It is usually faster and smoother to always load the dependency, especially if you intend to deliver the app as a single minified javascript file (e.g. using Sencha Cmd).

#section syntax instead of requirejs or browserify for angularjs application

I understand that requirejs and browserify can load my files dependent on its current context, and that it is amazing. I would really prefer to use the #section sections syntax that the razor engine uses. Was just wondering if there is a way to implement this into a typescript / angularjs application.
for example
index.html
#renderSection scripts;
// which could turn into something like
<script data-render="scripts"></scripts>
// the app.run() could declare all the scripts that will be needed on every
// page view
view.html
<script ng-section-repeat="injected in injection"></script>
// the ng-section-repeat is basically taking all the items in the
// typescript constructor and then finding out which ones are needed for
// that view.
I like the idea injecting application file dependencies in the view , without a configuration file and all the added extras that comes with the loaders.
I just want to easily define what files are needed in the actual view and get them loaded, with angular's dependency injection handling the dependency itself.
If you are handling all your dependencies with $inject then , as far as i can tell, dependency is technically already setup in the controllers, all one would need, is to load this as it is called. Which could even eliminate the need for the #section scripts completely
Update:
What i have done to sort of replicate the module loaders is to just use gulp-concat and define the file order in my gulp.config.js and then pass it to the gulp-src before running $.concat .this allows me to have the files in the gulp steam , in dependent order . They are however loaded on the first load. With gulp-uglify the files are tiny ( its now at 566Kb with 16 external libraries loading in 69ms . To put that into perspective it takes 209ms to load one google font ).
I dont know maybe i am not understanding browserify correctly but i honestly struggle to see the need for it, its seems extremely convoluted for something so simple
It is possible using external modules and an injector to do what you asked for:
I just want to easily define what files are needed in the actual view
import {UserFactory} from 'models/userFactory';
import {UserValidator} from 'models/userValidator';
import {Inject} from 'angular2/di';
and get them loaded, with angular's dependency injection handling the dependency itself.
Note: My example uses angular 2.x because I less familiar with angular 1.x and I'm sure you can do something really similar...
class SomeComponent {
userName: string;
userRating: number;
rating: number;
constructor(
#Inject(UserFactory) UserFactory
#Inject(UserValidator) UserValidator
)
{
this.UserFactory = UserFactory;
this.UserValidator = UserValidator;
}
}
Then you can use Browserify to create a bundle.js file that can be executed in a web browser.

(Jade or Slim)-like syntax for React?

I've gotten used to using slim and jade and I recently moved to writing applications on the front end with React and find that now I'm writing bloated HTML again inside my components. I'm currently using Ruby on Rails and .jsx files with babel, etc using:
gem 'react-rails', '~> 1.4.0'
gem 'react-router-rails', '~>0.13.3.2'
But I'm also using React with node and express using the react-starterify boilerplate and it's the same story with Node.
Is there anything that can allow me to start writing my html in React using a syntax like slim or Jade?
One thing to keep in mind is that JSX isn't HTML—it just looks like it. This is important because the JSX transpiler (usually Babel these days) takes the JSX syntax and modifies it from this:
<div className="container">
<p>Testing!</p>
</div>
to something like this:
React.createElement("div", { className: "container" },
React.createElement("p", null, "Testing!")
)
By abstracting over React.createElement calls, you can end up with a project like r-dom, which does the same thing but with a nicer syntax:
r.div({className: 'container'}, [
r.p('Testing!'),
])
or react-hyperscript, which allows an alternative syntax for some properties:
h('div.container', [
h('p', 'Testing!')
])
However, since JSX turns into plain JS calls, any language or syntax that can be converted into React.createElement calls will work great with React, as long as you set up your Rails asset pipeline so that it correctly does the transpilation as part of serving assets.
There is a project that does this with Jade's syntax called react-jade; there are a few differences from regular Jade and some unsupported features, but it may do what you want it to do. In a Rails project, you'd need to find or create a preprocessor that turns the appropriate Jade code into React-specific JS.
There's one other thing I wanted to mention based on a comment in your question:
now I'm writing bloated HTML again inside my components
If you're just talking about HTML's syntax then no problem, but if you're finding that the render methods of your React components are getting large and hard to manage, then it's probably a good sign that you need to break your component into smaller components (see "Tip 4: Embrace composition!" at this link).

unit testing angular directives templateUrl (include requirejs and ng-html2js

I am working on a not-yet-huge angular app. I have got a problem with karma unit testing. To make a long story short, I am using angular with requirej. My tests run well if I keep my template embedded in my directive. But as this app will be huge, it will not scale. But when I move the template and load it in my directive with templateUrl, I can not get rid of one of the following errors (depending on how I modify the conf):
(initial error) "Unexpected request: GET path/to/mytpl.html"
"ReferenceError: Can't find variable: angular". This error occurs with different config:
when I put {pattern: 'path/to/mytpl.html', included: false} in my karma.conf and add a requirejs's define path/to/mytpl.html in my test file (this is the way I would like that to work).
when I put path/to/mytpl.html in my karma.conf then all my template return this error (whether I am using ng-html2-js preprocessor or not).
"Error: [$injector:modulerr] Failed to instantiate module templates due to:
Error: [$injector:nomod] Module 'templates' is not available!". I saw that karma-ng-html2js-preprocessor could be use to create a module that will contains all templates, but it never worked.
NB: I do not use FQN in karma.conf, I use it here to be consistent. I do not think that matters, but I prefer to precise it
I saw someone putting each 'path/to/mytpl.html': {deps: ['angular']} in the shim section of their test-main.js. But it does not work for me and that solution will not scale.
I had the same problem and found solution in the following article.
I guess your problem is in wrong karma or requireJS config, in particular base path setup.
In case your directive templateUrl is relative and that url can't be resolved correctly against karma base path or requireJS base path, then you need to work out this issue.
Let's assume your directive has 'app/directive/myDirective.html' value for templateUrl. Then:
Check if your html file is properly included into files section of karma config, pay attention to included:false setting:
files: [
//...
{ pattern: 'path_to_directive/myDirective.html', included: false },
//...
],
of course you can use a wild card chars like * or **, but for troubleshooting purposes I would start with a full path value. According to the value of basePath in karma config, 'path_to_directive' will or won't be the same as path in your app.
Ex: if karma base path is one level up from you app root, then your 'path_to_directive' = 'some-folder/app/directive/myDirective.html'
Make sure you can get your file from the browser. Go to http://localhost:karma_port/base/path_to_directive/myDirective.html.js
While doing this test pay attention to:
base prefix in the url (karma serves all files from that virtual path and adds this prefix to all urls)
'.js' at the end of your url
Once you get file in step 2, you'll see the actual module code, created by html2js preprocessor. It should look something like this:
angular.module('module_path/myDirective.html', []).run(function($templateCache) {
$templateCache.put('module_path/myDirective.html',
//...
Make sure module_path is 'app/directive/'. If not then you have 2 options:
module_path has some prefix => simply remove it by adding the following to karma config:
ngHtml2JsPreprocessor: { stripPrefix: 'modulePrefix' }
module_path is shorter => add missing part:
ngHtml2JsPreprocessor: { prependPrefix: 'missingPart' }
Restart karma and make sure 'module_path' in step 3 is 'app/directive/'
Make sure you have added angular dependency for your template module (check test-main.js which is basically config file for requireJS). Also keep in mind, that value in shim section will be resolved according to baseUrl value in your test-main.js file. For simplicity, I'll assume that requireJS can get the file from 'requirejs_path/myDirective.html' path.
shim: {
//..
'requirejs_path/myDirective.html': { deps: ['angular'] },
//..
}
Make sure you have included your template as a dependency to your test and also don't forget to load the module:
define(['requirejs_path/myDirective.html', 'angular-mocks', '...'], function () {
describe('test suite', function() {
//...
beforeEach(module('app/directive/myDirective.html'));
//...
});
});
I hope by completing these 6 step you'll get a working test. Good luck.
After having set aside this problem for a while and use this time to improve my knowledge and to widen my app, I found a solution.
Brief methodology recap: as karma-ng-html2js-preprocessor or karma-html2js-preprocessor seems to work on some other projects and as they are referenced in the official doc, I tried many (almost all, I think) possible configuration combinations to run one of them in my test environment. With no success, I came the clue : I have to drop them and find another way.
The solution came from grunt-html2js. By simply adding to grunt configuration the basic set-up and run grunt, it generate a module containing all my templates - maybe, I will split it later in more meaningful modules. But from here, work is almost done. Then I just had to :
tell karma to serve the file has other external tools file: [ {pattern: 'path/to/mytplmodule.js', included: false}, ...]
register the path in my require conf for tests path: [ 'templates' : 'path/to/mytplmodule', ...]
shim it, still in require conf for tests, to be sure it won't be loaded before angular shim: [ 'templates' : 'angular', ...]
load this module in my test file define(['templates',...], function(){...});
retrieve the tested directive's template beforeEach(module('path/to/mytpl.html'));
add a step in your grunt configuration to make it automatically generated before running tests.
That's all !
There is a drawback with this approach, it requires to retrieve or mock all other directives' templates used in the tested directive. It could be boring as number of directives grows. But I prefer that for now as it is not invasive for the application's code. Maybe later, especially when I will include javascript's compilation in my process, I will move it from test to app too.

AngularJS - parameters in css files

I'm currently migrating from custom framework to Angular. Since we've got legacy, all front-end resources like stylescheets, images and scripts should be located on a subdomain, and all urls should be absolute. I've got a bunch of css files with a parameter specifying our static domain. I'm looking for a native Angular approach to using parameters in css, so I'll be able to write smth like:
.body {background: "{{domain}}/img/bg.png";}
Currently in our framework styles are loaded with, say, $http.get(), then processed with .replace and then appended to DOM.
Any ideas?
Thank you.
Try the $interpolate service. Inject it in a method, then use like this:
var fn = $interpolate(cssText);
var processedCssText = fn(scope); // scope is whatever obj that contains `domain` and other properties that might be used inside cssText
You can even configure the opening & closing symbols, if needed. See the documentation for $interpolate for more information.
You want LESS.
http://lesscss.org
It's the "dynamic stylesheet language".

Resources