Package CSS with AngularJS Module - javascript

I am making a stand alone module that I would like to use in multiple projects or maybe make a bower package out of. I figured out how to package the html templates by converting them to js but I can't figure out a good way to package some CSS with it.
I tried out angular-css and it does what I need with one downfall, it requires me to specify the file path to the CSS relative to the index.html. This is a problem as the file structures may be different in different projects and I would like this to be packaged in a way that it can just be added to the project and work.
As it stands I'm adding CSS with the method below but I'm not sure this is really a good way to do it. I can add the CSS this way and only add it once but I cant remove it when it's not needed
In the directives controller I use this code to add CSS styles
var markerAnim = document.getElementById('markerAnim');
if (!markerAnim) {
var markerStyles = {
animation: 'some css',
locMarker: 'more css',
after: 'a little more css'
};
var styleSheet = markerStyles.animation + markerStyles.locMarker + markerStyles.after;
angular.element(document).find('head').prepend('<style id="markerAnim" type="text/css">' + styleSheet + '</style>');
}

Related

How can I enable tailwind intelliSense outside of "className"?

I'm using the tailwind CSS intellisense vscode extension, and it seems to only work when typing inside a className property.
I'm using a package called cntl https://www.npmjs.com/package/cntl to help write more maintainable classNames, however, using this package, I lose the intelliSense support.
Is there any way to manually configure so that I get the intelliSense when writing my cntl?
const title = cntl`
text-3xl
// I'd like intellisense here
`
Here's how I solved it.
In VSCode settings.json add the following:
"tailwindCSS.experimental.classRegex": [
"cntl`([^`]*)", // cntl`...`
],
I realize this Q is old, but it still shows up in search so I wanted to share my workflow :)
Here's my VS Code settings.json to add Tailwind IntelliSense within objects and variables who's name ends with "Classes":
"tailwindCSS.experimental.classRegex": [
["Classes \\=([^;]*);", "'([^']*)'"],
["Classes \\=([^;]*);", "\"([^\"]*)\""],
["Classes \\=([^;]*);", "\\`([^\\`]*)\\`"]
],
Tailwind IntelliSense will now recognize all of the following strings:
const defaultClasses = `text-grey`;
const componentClasses = {
default: 'text-grey',
danger: `text-red`,
warning: "text-yellow",
};
Note: the regex matches code blocks that start with Classes = and ends with ; — you can replace Classes = with your own matcher, like. cntl :)
Linting is not supported yet as per: https://github.com/tailwindlabs/tailwindcss/issues/7553. Hover seem to be supported now though
For clsx
"tailwindCSS.experimental.classRegex": ["clsx\\(([^)]*)\\)"]
For classnames
"tailwindCSS.experimental.classRegex": ["classnames\\(([^)]*)\\)"]
None of the answer worked for me.
But it workes based on the guide from https://www.jussivirtanen.fi/writing/using-tailwind-intellisense-within-strings
If you're using VS Code and writing your Tailwind classes within a variable, you've probably noticed that Tailwind IntelliSense doesn't work. However, if you're using a consistent naming scheme, you can fix this issue.
I use a consistent naming scheme with the word Styles at the end. In this case, I can go to Tailwind IntelliSense settings and add .*Styles to the tailwindCSS.classAttributes array:
// settings.json within VS Code
{
// Add ".*Styles" (or whatever matches your naming scheme)
"tailwindCSS.classAttributes": ["class", "className", "ngClass", ".*Styles"]
}
example usage
const contentStyles = "py-1 bg-white dark:bg-gray-700"
This will detect a container consisting of className = [] string and its variants such as ButtonClassNamesXyz Classname and whatever is inside [ ] will be processed.
"tailwindCSS.experimental.classRegex": [
["\\S*[Cc]lass[nN]ame\\S* = \\[([\\s\\S]+?)(?=][\\s;,.])", "'([^']*)'"],
"return '(.*)'",
],
Adjust regex here https://www.debuggex.com/r/yhCYrsFdzXRWQEhP
v2 note
I have added detection for ] inside the actual classname string.
tailwindlabs
/
tailwindcss
: [IntelliSense] Custom class name completion contexts #7554
I understand this question has been answered, but I was still facing some trouble because I wanted to use Tailwind's intellisense with react's classnames library.
Here's what worked for me after adding it to VSC's settings.json:
"tailwindCSS.experimental.classRegex": ["classNames\\(([^)]*)\\)"],

Rails 6 Webpacker calling javascript function from Rails view

I have following structure for Javascript in my Rails 6 app using Webpacker.
app/javascript
+ packs
- application.js
+ custom
- hello.js
Below shown is the content in the above mentioned JS files
app/javascript/custom/hello.js
export function greet(name) {
console.log("Hello, " + name);
}
app/javascript/packs/application.js
require("#rails/ujs").start()
require("jquery")
require("bootstrap")
import greet from '../custom/hello'
config/webpack/environment.js
const { environment } = require('#rails/webpacker')
const webpack = require('webpack')
environment.plugins.prepend('Provide',
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
Popper: ['popper.js', 'default']
})
)
module.exports = environment
Now in my Rails view I am trying to use the imported function greet like shown below
app/views/welcome/index.html.haml
- name = 'Jignesh'
:javascript
var name = "#{name}"
greet(name)
When I load the view I am seeing ReferenceError: greet is not defined error in browser's console.
I tried to search for a solution to this problem and found many resources on web but none turned out to help me. At last when I was drafting this question in the suggestions I found How to execute custom javascript functions in Rails 6 which indeed is close to my need however the solution shows a workaround but I am looking for a proper solution for the need because I have many views which needs to pass data from Rails view to JS functions to be moved custom files under app/javascript/custom folder.
Also I would highly appreciate if anybody can help me understand the cause behind the ReferenceError I am encountering.
Note:
I am not well-versed in Javascript development in Node realm and also new to Webpacker, Webpack, Javascript's modules, import, export, require syntax etc so please bear with me if you find anything silly in what I am asking. I have landed up in above situation while trying to upgrade an existing Rails app to use version 6.
Webpack does not make modules available to the global scope by default. That said, there are a few ways for you to pass information from Ruby to JavaScript outside of an AJAX request:
window.greet = function() { ... } and calling the function from the view as you have suggested is an option. I don't like have to code side effects in a lot of places so it's my least favorite.
You could look at using expose-loader. This would mean customizing your webpack config to "expose" selected functions from selected modules to the global scope. It could work well for a handful of cases but would get tedious for many use cases.
Export selected functions from your entrypoint(s) and configure webpack to package your bundle as a library. This is my favorite approach if you prefer to call global functions from the view. I've written about this approach specifically for Webpacker on my blog.
// app/javascript/packs/application.js
export * from '../myGlobalFunctions'
// config/webpack/environment.js
environment.config.merge({
output: {
// Makes exports from entry packs available to global scope, e.g.
// Packs.application.myFunction
library: ['Packs', '[name]'],
libraryTarget: 'var'
},
})
// app/views/welcome/index.html.haml
:javascript
Packs.application.greet("#{name}")
Take a different approach altogether and attach Ruby variables to a global object in your controller, such as with the gon gem. Assuming you setup the gem per the instructions, the gon object would be available both as Ruby object which you can mutate server-side and in your JavaScript code as a global variable to read from. You might need to come up with some other way to selectively call the greet function, such as with a DOM query for a particular selector that's only rendered on the given page or for a given url.
# welcome_controller.rb
def index
gon.name = 'My name'
end
// app/javascript/someInitializer.js
window.addEventListener('DOMContentLoaded', function() {
if (window.location.match(/posts/)) {
greet(window.gon.name)
}
})
#rossta Thanks a lot for your elaborate answer. It definitely should be hihghly helpful to the viewers of this post.
Your 1st suggestion I found while searching for solution to my problem and I did referenced it in my question. Like you I also don't like it because it is sort of a workaround.
Your 2nd and 3rd suggestions, honestly speaking went top of my head perhaps because I am novice to the concepts of Webpack.
Your 4th approach sounds more practical to me and as a matter of fact, after posting my question yesterday, along similar lines I tried out something and which did worked. I am sharing the solution below for reference
app/javascript/custom/hello.js
function greet(name) {
console.log("Hello, " + name)
}
export { greet }
app/javascript/packs/application.js
require("#rails/ujs").start()
require("bootstrap")
Note that in above file I removed require("jquery"). That's because it has already been made globally available in /config/webpack/environment.js through ProvidePlugin (please refer the code in my question). Thus requiring them in this file is not needed. I found this out while going through
"Option 4: Adding Javascript to environment.js" in http://blog.blackninjadojo.com/ruby/rails/2019/03/01/webpack-webpacker-and-modules-oh-my-how-to-add-javascript-to-ruby-on-rails.html
app/views/welcome/index.html.haml
- first_name = 'Jignesh'
- last_name = 'Gohel'
= hidden_field_tag('name', nil, "data": { firstName: first_name, lastName: last_name }.to_json)
Note: The idea for "data" attribute got from https://github.com/rails/webpacker/blob/master/docs/props.md
app/javascript/custom/welcome_page.js
import { greet } from './hello'
function nameField() {
return $('#name')
}
function greetUser() {
var nameData = nameField().attr('data')
//console.log(nameData)
//console.log(typeof(nameData))
var nameJson = $.parseJSON(nameData)
var name = nameJson.firstName + nameJson.lastName
greet(name)
}
export { greetUser }
app/javascript/packs/welcome.js
import { greetUser } from '../custom/welcome_page'
greetUser()
Note: The idea for a separate pack I found while going through https://blog.capsens.eu/how-to-write-javascript-in-rails-6-webpacker-yarn-and-sprockets-cdf990387463
under section "Do not try to use Webpack as you would use Sprockets!" (quoting the paragraph for quick view)
So how would you make a button trigger a JS action? From a pack, you add a behavior to an HTML element. You can do that using vanilla JS, JQuery, StimulusJS, you name it.
Also the information in https://prathamesh.tech/2019/09/24/mastering-packs-in-webpacker/ helped in guiding me to solve my problem.
Then updated app/views/welcome/index.html.haml by adding following at the bottom
= javascript_pack_tag("welcome")
Finally reloaded the page and the webpacker compiled all the packs and I could see the greeting in console with the name in the view.
I hope this helps someone having a similar need like mine.

How to template dynamically generated react code?

I'm currently working on a framework for a web app that a number of developers will be working on. The web app will be in a dashboard style, with individual modules or "views" written in React.
One of the requirements of the framework is that it must ship with a tool that allows developers to automatically generate a new module or "view", so that it creates the files and folders needed and they can get straight to work on the code logic.
An extremely simple flow would be as follows:
Developer enters the name of their new module as an argument to a npm script
A script runs which creates [moduleName.js] and [moduleName.less], links them together, places them in a directory, and writes the generic react code.
I'm now up to the point where I am generating the common react code. Here is what I wrote:
function writeBoilerplate(moduleName) {
var jsFileStream = fs.createWriteStream("./src/m-" + moduleName + "/" + moduleName + ".js");
jsFileStream.once('open', (fd) => {
jsFileStream.write("import React from \"react\"\;\n");
jsFileStream.write("import Style from \"\.\/" + moduleName + "\.less\"\;");
jsFileStream.write("\n");
jsFileStream.write("\n");
jsFileStream.write("export default class " + moduleName + " extends React\.Component \{\n");
jsFileStream.write("\n");
jsFileStream.write(" constructor\(\) \{\n");
jsFileStream.write(" super\(\)\;\n");
jsFileStream.write(" \}\n");
jsFileStream.write("\n");
jsFileStream.write(" componentDidMount\(\) \{");
jsFileStream.write(" \}");
jsFileStream.write("\}");
jsFileStream.end();
});
You can immediately see the problem here, and I stopped before going too far. If I continue on this path, the code will become unreadable and unmanageable.
I want to refactor this to use javascript templates. However, I have never used templating before and I am unsure of how to create a template and use it, or if there are any tools to help.
How can I refactor this code to use a template?
You need to use a template library for that. You can try lodash template one, for example: https://lodash.com/docs#template.
Put your boilerplate in a template file, read it and use something like:
var compiled = _.template(templateFileContent);
compiled({ 'moduleName': 'mymodule' });

Best practices for importing deeply nested Javascript components

I am working on a Ruby/React project. We are using React components and CoffeeScript and final JS is assembled by Sprockets:
#= require org/components/whatever
{ Whatever } = Org.Components
It is okay when there is not too much nesting and then you are wrtiting something like this:
#= require org/components/whatever
#= require org/components/something/else/whatever
{ Whatever1 } = Org.Components
{ Whatever2 } = Org.Components.Something.Else
Today I was trying to find where Org.Components.Image.Upload is used. But sometimes it is imported as { Upload } or used as Image.Upload and it doesn't make things easier.
Now I am thinking maybe don't go further than Org.Components for imports. So if you need Image.Upload — get { Image } = Org.Components, and use Image.Upload. If it gets too long - assign to a variable.
#= require org/components/image
{Image} = Org.Components
Upload = Image.Super.Complex.Upload
# Or use it like this for explicitness
render: ->
Image.Super.Complex.Upload
What is the best practice here? I want code to be searchable.
If you are in a CommonJS environment (Node), and probably using a module bundler like Webpack or Browserify you can take advantage of the direct module imports. For example:
Instead of doing this Org.Components.Image, you can do that:
import Upload from 'org/components/Image/Super/Complex/Upload'
// or var Image = require('org/components/Image/Super/Complex/Upload');
In your original strategy you load the entire library (org) to further filter it down to Upload.
In the proposed solution above, you only load the Image module and nothing else. This will probably save you a lot of bits in your final footprint, specially if org contains a big pile of components used inside your company.
To stop fighting against the sprockets I have defined a Root component that tells Sprockets where to look for the subcomponents:
# components/branding.coffee
#= require_tree ./branding
Org.Components.Branding = {}
So now if I need anything from the branding subtree I simply do the following:
#= require org/components/branding
{div} = React.DOM
{Branding} = Org.Components
Org.defineComponent "Settings.Branding",
render: ->
div {},
Branding.Edit.ContactUs {}
Branding.Config {},
Branding.Edit
This way I don't need to worry about dependencies and found it to be much more pleasant to work with.
I would suggest that this approach helps refactoring as you don't have to change multiple requires everywhere.
Branding.Config is a data-wrapped that loads and syncs settings. In the example above it is used to load settings for the Brading.Edit page. And here it is loading branding for in 'Layouts.Default'.
And again I only require branding
# apps/src/org/components/layouts/default.coffee
#= require org/components/branding
{Branding, TenantStateBar, UnsupportedBrowserBar} = Org.Components
Org.defineComponent 'Layouts.Default',
render: ->
div {},
Branding.Config {},
Branding.Style

CSS in an angular directive

I'm writing an Angular directive to display some information about a music album, currently it shows the information below the album art, but if the element gets too small then it should shrink the album art and put the information next to it. At the moment I just have the html in the page directly and have css to do the changes in the main page, but this causes the page to be quite monolithic as it also display other things, which is why I want to seperate it out into directives.
However I can't see how to include CSS in the directive, I don't want to include it inline in the html, and I could put a style tag in the html and put it in there, but then it would be repeated every time I use the directive. Is there some way of injecting a link to a CSS file into the head from the directive? Like there is a templateUrl field is there a stylesheetUrl or something?
You could check this module: angular-css-injector.
Be warned it's currently only compatible with angular 1.2.x...
You can inject css in your directive like this:
var head = document.getElementsByTagName('head')[0];
var cs = document.createElement('link');
cs.rel = 'stylesheet';
cs.href = 'css/myStylesheet.css';
head.appendChild(cs);
So a directive would look like this:
app.directive('myDirective', function () {
var head = document.getElementsByTagName('head')[0];
var cs = document.createElement('link');
cs.rel = 'stylesheet';
cs.href = 'css/myStylesheet.css';
head.appendChild(cs);
return {
templateUrl:'templates/myTemplate.html',
link: function (scope, elm, attrs, ctrl) {
}
};
});
Structuring an angular app is one of the hardest things about learning angular. Angular tries hard to modularise code, but (the current state of) html, css, and javascript doesn't really allow you to package the things together, so you have to find a way that works well for you.
The way I keep things separate (yet together) is generally using my build system (I use gulp), and a CSS preprocessor (for me, Stylus).
My process for creating a new directive is as follows:
Define a new angular module (in its own file) my-albums.coffee:
angular.module('my-albums', [])
.directive('myAlbumDirective', ()->
restrict: 'A'
templateUrl: 'SomeTemplate.jade'
# etc.
)
Create a jade template my-album-directive.jade
.album
img(ng-src="{{album.imageUrl}})
span.name {{album.name}}
Create the stylus file with the same name as the module prefixed with an underscore: _my-albums.styl. In here I will include module specific css.
[myAlbumDirective]
.album
display flex
flex-direction column
#media screen and (min-width: 600px)
flex-direction row
Then, whenever I import an angular module into my app.coffee (which contains a long list of module imports), I also import its style in my main.styl stylesheet:
#import '../my-albums/_my-albums.styl'
When I run my build system, it (among other things):
Automatically compiles .jade files into a app.templates angular module (pre-populating the $templateCache (app.templates is included in the imports in app.coffee
Compiles and concatenates all coffeescript into script.js
Compiles and concatenates all stylus files whose filenames do not begin with an underscore into style.css
Then inside my index page I just have two imports:
script(src='js/app.js')
link(rel='stylesheet', href='css/style.css')
TL;DR:
There's no easy way of keeping your directive code separate from the rest of the page, but if you research build systems and other people's angular project structures, you'll find something you like.
Note
SoonTM things will be neater (see web components and angular 2.0)

Categories