8

I am using CSS Modules in an Angular Project with Webpack. I had already transformed the class names in .css and .scss files with postcss-modules.

Then with posthtml-css-modules I had changed the values on the class property in html elements for his hash value defined by postcss-modules.

I can say that everything is working fine .

Now, I have a new challenge to resolve.

Angular, has a feature that allows you to change dynamically the value of class in a html element depending on a condition:

https://angular.io/api/common/NgClass

For Example, I can do:

<div [className]="myVar ? 'myClass1' : 'myClass2' " >

If myVar = true, the html element will be:

<div class="myClass1" >

And if myVar = false, the html element will be:

<div class="myClass2" >

Like I do not know what is going to be the value of myVar during compilation time (because the value of myVar depends on user actions) I am not able to set the value for <div css-module="myClass1" > or <div css-module="myClass2" > in order to hash the class names of myClass1 or myClass2.

BUT (Here comes my solution)...

If I can invoke the same function that does [hash:base64:5] (https://github.com/css-modules/postcss-modules#generating-scoped-names)

I can create a function that receives a string as parameter and return the class name as a hash.

It would be something like this:

<div [className]="setMyClassNameInHash(myVar)">

Then in javascript:

function setMyClassNameInHash(condition) {
      return  condition ? doHash64('myClass1') : doHash64('myClass1');
}

doHash64() would be the function that takes a string and returns the hash using [hash:base64:5].

In conclusion, my question is:

¿How I can invoke the same function that does [hash:base64:5] in a javascript file?

Thanks!!!!

German Quinteros
  • 1,870
  • 9
  • 33

1 Answers1

1

You'll need to integrate a templating step into your build process. The plugin exports the class names and their mapped names to a json file, so you can look up the hashed class name from the original.

Edit: Since the built in templating only works for a single class name and doesn't appear to support replacing class names in things like angular attributes, you could do the templating yourself using a templating library like lodash. If you're already using grunt or gulp, I'd recommend using their template tasks instead of this manual way because they do a lot of things for you, like supporting multiple files.

In your html, you would use lodash delimiters to get the hashed class name like:

<div [className]="myVar
     ? '<%= getHashedClass('myClass1') %>'
     : '<%= getHashedClass('myClass2') %>' " >

Your node build script might look like this:

var fs = require('fs');

postcss([
  require("postcss-modules")({
    // Callback to get hashed class names
    getJSON: function(cssFileName, classNames, outputFileName) {
      var filePath = '/path/to/outputDir/file.html';

      // Function to lookup hashed class names
      var getHashedClass = function(unhashedClass) {
        return classNames[unhashedClass];
      }

      // Read your html file as a string
      var html = fs.readFileSync(path),

      // Use lodash to template it, passing the class lookup function
      var compiled = _.template(html);
      var templated = compiled({
        getHashedClass: getHashedClass
      });

      // Write the templated html
      fs.writeFileSync(path, templated);
    }
  })
]);
frodo2975
  • 10,340
  • 3
  • 34
  • 41
  • Hi @frodo2975, I have already done what you say. My issue is other, I cannot hash the class value on the html when the class is set dynamically in Angular. One option is to take the value from the json file, but that option is not scalable because I cannot upload more than 30 json files in the production environment. – German Quinteros Mar 05 '20 at 18:47
  • I think you're missing something, if I'm understanding it right, it does the swap at compile time. So after you build your app, your html should have the 2 hashed class names substituted in. Loading a file at runtime would be unnecessary. – frodo2975 Mar 06 '20 at 01:05
  • Yes, both classes are being hashed in the ```.scss``` file. However, when I am going to change the class name in the ```html``` through posthtml-css-modules, it doesn't have a directive for dynamic class, because ```css-module``` only applies for class names that are not going to change their value during run time. Did I explain better the problem? Thanks for your help. – German Quinteros Mar 06 '20 at 16:04
  • 1
    After looking at it again, I think I see what you're saying. Since it uses a `css-module` attribute, it's not a literal "find/replace" for the class, so it won't work for js strings in html attributes. I'll update my answer – frodo2975 Mar 06 '20 at 18:30
  • Thanks for your response. I will try it and let you know if it works. – German Quinteros Mar 06 '20 at 20:08
  • your solutions work. The only issue that it has is that it overrides the original file and remove the ```'<%= getHashedClass('myClass1') %>'``` for the hash. But I can create a no compiled file and another one after compiled. – German Quinteros Mar 12 '20 at 15:40
  • 1
    Cool, if you don't want it to overwrite, then just change where you write the templated html. – frodo2975 Mar 12 '20 at 22:27
  • thanks for that advice. I am using webpack as compiler. So, after the posthtml-css-modules executes, I execute my custom webpack loader that changes the class names for the hashed class names using the script that you provide me. With that solution I am not changing the original HTML. Thank you very much for all your help!!! – German Quinteros Mar 26 '20 at 14:16