5

TL;DR

I need to make Angular evaluate/parse HTML elements with Angular markup that are dynamically added to the DOM by treant-js.

[UPDATE 30/09/17]

I've followed the solution proposed there (which involves dynamic module/component creation). However, there are more and more issues with this implementation.

First, I'm injecting to the sub-component the needed dependances declared for the parent module (Router, ActivatedRoute, etc.):

this.addComponent(
  this.treeContainer.nativeElement.outerHTML,
  {},
  this.conversation.threads,
  this.route,
  this.router,
  this.ws
);

This seems wrong to me, because the most logical way would be to inject these dependancies directly into the dynamic component (with imports in the dynamic module), but I couldn't manage to do this.

Also, because treant-js needs to directly attach to the DOM the new HTML components it creates (I guess in order to bind the events properly), I'm unable to retrieve the HTML string before it's in the DOM. So, the ugly fix I found was:

  1. to extract the HTML string with this.treeContainer.nativeElement.outerHTML(see the code snippet above) then send it as the template parameter for the dynamic component ;
  2. since there would be 2 trees (because of 1.), to remove the initial treeContainer handled by treant-js: $(this.treeContainer.nativeElement).remove();.

Where then are we?

Besides the issues mentionned earlier, the solution I've tried is not at all acceptable. It seems that treant-js needs to attach its HTML elements to the DOM for event bindings and tree storage/management, so when I duplicate the HTML string of the tree to form the template of a dynamic component, this 2nd tree is well evaluated by Angular, but not under treant-js framework anymore (e.g.: some functionalities like collapsable handles would no longer work).

To sum up: I'm unable to have both Angular and treant-js working at the same time to handle my tree markup.

Moreover, the ugly trick I use to avoid having duplicate trees causes other issues, when it comes to re-generate the tree, whether it be from a dynamic update (new node created by user interaction), or from navigating to the same page multiple times (routing).

Last but not least, I can't even use some Angular markup like ngModel which come from imported modules (e.g. FormsModule): I get an error saying ngModelisn't bound to input element. The module is of course imported in my parent component, and I tried to import it in the dynamic module as well:

private addComponent(template: string, properties: any = {}, threads: Thread[],
                   route: ActivatedRoute, router: Router, ws: WebSocketService) {

@Component({template})
class TemplateComponent implements OnInit {

//...

@NgModule({
  declarations: [TemplateComponent],
  imports: [FormsModule]
})
class TemplateModule {}

const module    = this.compiler.compileModuleAndAllComponentsSync(TemplateModule);
const factory   = module.componentFactories.find(componentFactory =>
  componentFactory.componentType === TemplateComponent
);
const component = this.container.createComponent(factory);
Object.assign(component.instance, properties);

Unless I find soon a way to make both Angular and treant-js work together smoothly, I may have to use another library such as angular-tree-component or ng2-tree. The big downside then is that I may miss some visual functionalities (like tree orientation) and interactivity I found easy to implement with treant-js.

[First post]

I'm new in Angular2, so there are certainly concepts I still don't understand correctly. I'm using treant-js in my own Angular2 project, and I'm stuck at making Angular2 evaluate/compile the directives in the generated HTML by treant-js.

I've seen this : Integrating Treant-js in Angular2 Project to integrate treant-js into the project, but I can only use some static HTML instead of routerLink, ngIf or ngFor for instance.

I call buildConversationTree in the ngInit of my component :

buildConversationTree() {
    const config = {
      container: '#tree',
      connectors: {
        type: 'step'
      },
      node: {
        HTMLclass: 'thread-node',
        collapsable: true
      },
      levelSeparation: 45
    };

    let treeConfig = this.buildNodes(this.conversation.threads);
    treeConfig.unshift(config);
    const tree = new Treant(treeConfig, this.onTreeLoaded, $);
  }

Basically, the buildNodesmethod generate the HTML for each node of the tree, by calling the nodeHTMLmethod :

nodeHTML(data) {
    return `
    <div id="thread${data.id}" class="thread-node-wrapper">
      <a class="main-link" routerLink="../thread/${data.id}">
        Lorem ipsum
      </a>
    </div>
    `;
}

I've got a simple template for my component:

<div class="tree-container">
  <div id="tree"></div>
</div>

which becomes after the tree generation:

<div class="tree-container">
  <div id="tree" class=" Treant Treant-loaded"><svg height="598" version="1.1" width="633" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="overflow: hidden; position: relative;"><desc style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);">Created with Raphaƫl 2.1.4</desc><defs style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"></defs></svg><div class="node thread-node" style="left: 191.5px; top: 224px;">
<div id="thread60" class="thread-node-wrapper">
  <a class="main-link" routerlink="../thread/60">
    Lorem ipsum
  </a>
</div>
</div></div>
</div>

And the routerlink="../thread/60"remains uncompiled/recognized by Angular.

In Angular.js: Back in Angular.js (which was used for the project before), we would solve the problem using $compile :

var treeIsLoaded = function () {
    // ...
    var e = $('#tree')[0];
    $compile(e)($scope);
    if (!$scope.$$phase) {
        $rootScope.safeApply();
    }

    // ...

};

and all the directives like ng-click, ng-show, etc., worked out, so I'm a bit puzzled about how hard it is to make it work in Angular2.

What I tried:

Pyriceti
  • 66
  • 1
  • 5

0 Answers0