Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding host attribute to generated DOM elements

I am trying to integrate d3 and angular2 alpha.37 (started from here). The problem I am currently having is that the generated DOM elements don't get the attributes used in the emulated styling view encapsulation, and so I can't style them without setting the view encapsulation for the element to None (or Native, but I would rather use emulated).

I managed to programmatically extract the required attribute from an element inside the component [1], and then add it to the generated elements[2], which does work, but this is clearly incredibly hacky:

import {Component, View, Attribute, ElementRef, LifecycleEvent} from 'angular2/angular2';

import d3 from 'd3';

@Component({
  selector:   'bar-graph',
  properties: [ 'data' ]
})
@View({
  template: '<div class="chart"></div>',
  styles: [`.chart {
    background: #eee;
    padding: 3px;
  }

  div.bar {
    width: 0;
    transition: all 1s ease-out;
    -moz-transition: all 1s ease-out;
    -webkit-transition: all 1s ease-out;
  }

  div.bar {
    font: 10px sans-serif;
    background-color: steelblue;
    text-align: right;
    padding: 3px;
    margin: 5px;
    color: white;
    box-shadow: 2px 2px 2px #666;
  }`]
})
export class BarGraph implements LifecycleEvent.OnChanges {
  data: Array<number>;
  divs: any;
  constructor(elementRef: ElementRef, @Attribute('width') width: string, @Attribute('height') height: string) {


    var el:any    = elementRef.nativeElement;
    var graph:any = d3.select(el);

    this.hostAttr = graph[0][0].children[0].attributes[1].name; //hack here [1]

    this.divs = graph.
      select('div.chart').
      style({
        'width':  width  + 'px',
        'height': height + 'px',
      }).
      selectAll('div.bar');

  }

  render(newValue) {
    if (!newValue) return;

    this.divs.data(newValue)
        .enter().append('div')
          .classed('bar', true)
          .attr(this.hostAttr, true) //add the attribute here [2]
          .style('width', d => d + '%')
          .text(d => d + '%');

  }

  onChanges() {
    this.render(this.data);
  }

}

Is there a recommended way to deal with this sort of thing (or should I stop tinkering with the DOM outside of Angular2)?

like image 838
RichL Avatar asked Oct 05 '15 13:10

RichL


People also ask

How do I add attributes to a DOM element?

We can add an attribute to a DOM element using two methods. First, is the setAttribute(name, value) and second is the setAttributeNode(attribute_node) .

Can I add my own attributes to HTML elements?

If you want to define your own custom attributes in HTML, you can implement them through data-* format. * can be replaced by any of your names to specify specific data to an element and target it in CSS, JavaScript, or jQuery.

What method allows to change the attribute of a DOM element?

To change the attribute value of an HTML element HTML DOM provides two methods which are getAttribute() and setAttribute(). The getAttribute() is used to extract the current value of the attribute while setAttribute() is used to alter the value of the attribute.


1 Answers

Not a complete answer (yet), but maybe some useful information that can help in finding a solution:

  • The issue still exists in beta.8. Setting view encapsulation to None and using global styles was the only solution that I could get to work. Using Native seemed to result in no elements being added to the DOM at all, but I'd have to do some more tests to figure out the cause.
  • The hack proposed by OP works, and could be refactored into a reasonable solution, at least in my opinion.
  • In the specific case of d3.js, things get even more tricky when introducing elements that are created internally by the library, e.g. through the methods in the d3.svg namespace. Undoubtedly a workaround for that could be found as well.
  • I believe that the issue is larger than d3 however. There are plenty of libraries out there that have their own DOM generation/manipulation mechanisms, and it is not unrealistic to think that a portion of those will at some point be integrated in some or other angular2 application. In that sense, it's surprising that the issue does not seem to have come up yet (or maybe my Google-fu is particularly weak this week).

In terms of solutions, these are the two approaches that I'm currently considering in case no one comes up with something nicer:

  1. Implement some sort of post-processor that walks a partial DOM tree and sets the style scope attributes. Maybe as a directive?
  2. Try to decorate the renderer, as hinted here.
like image 109
Robby Cornelissen Avatar answered Oct 17 '22 01:10

Robby Cornelissen