Angular (1.5.8) Dynamic Components

2019-03-09 15:55发布

I'm trying to build a sort of dynamic dashboard using Angular 1.5.8. I've made decent progress up until the final hurdle. Which is actually rendering the dynamic component.

I've tried 2 options, either adding a ui-view and programatically passing in the name of the widget, or, and this is the route I'm guessing is more correct, I need to figure out how to render a dynamic widget.

For Example: As I append and item to the dashItems collection, it should render a new widget (based on the name I've provided)

I have seen that I can swap out templates using ngInclude, but I'm still unclear as to how to get a template and controller to be injected dynamically. (EG all my templates wont be sharing a common controller).

JavaScript:

angular
    .module('myDashboard', [])
    .config(routesConfig)
    .component('dashboard', {
        templateUrl: 'dashboard/dashboard.tpl.html',
        controller: dashboardController
    })
    .component('widgetPie', {
        template: '<h3>Pie Graph</h3>',
        controller: function($log) {
            $log.info('widgetPie: loaded');
        }
    })
    .component('widgetLine', {
        template: '<h3>Line Graph</h3>',
        controller: function($log) {
            $log.info('WidgetLine: loaded');
        }
    });

function routesConfig($stateProvider) {
    // this is only needed if I go the ui-view route.. I assume
    $stateProvider
        .state('widgetPie', { component: 'widgetPie'})
        .state('widgetLine', { component: 'widgetLine'});
}

function dashboardController($log) {
    $log.info('in dashboard');
    this.dashItems = [
        { widget:'widgetPie' },
        { widget:'widgetLine' }
    ];
}

Markup (dashboard/dashboard.tpl.html):

<div>
    <ul>
        <li ng-repeat="item in dashItems">
            <!--somehow render dynamic-->
            <!--<widget-pie></widget-pie>-->
            <!--<div ui-view="item.widget"></div>-->
        </li>
    </ul>
</div>

Question(s):

1. I've looked into ngInclude, but to be perfectly honest, I'm not sure how to go about using it in this instance, and IF it is the right tool for this, or am I approaching this incorrectly?

2. Should I even be adding items to the state provider for this, EG i / could a widget be seen as a child state (thus I'm not sure what would be seen as best practice)

2条回答
Ridiculous、
2楼-- · 2019-03-09 16:09

I ended up changing the dashboard.tpl.html file to:

<div>
    <ul>
        <li ng-repeat="item in dashItems">
            <div ng-include="item.widget"></div>
        </li>
    </ul>
</div>

But I also needed to add a build task to run through my widgets folder and generate the following (or you can manually add, whatever floats your boat I guess).

angular
 .module('myDashboard')
 .run(function ($templateCache) {
    $templateCache.put('widgetPie', '<widget-pie></widget-pie>');
    $templateCache.put('widgetLine', '<widget-line></widget-line>');
 });

The above allows me to either use templateUrl, or inline templates.

.component('widgetPie', {
   templateUrl: 'dashboard/widgetPie.tpl.html',
   controller: function($log) {
      $log.info('widgetPie: loaded');
   }
})
.component('widgetLine', {
    template: '<h1>Some Content</h1>',
    controller: function($log) {
      $log.info('widgetLine: loaded');
    }
})
查看更多
霸刀☆藐视天下
3楼-- · 2019-03-09 16:20

You can do it. Firstly, you need to use wrapper component which helps you compile your dynamic component:

app.component('dynamicWrapper',
    {
        controller: function widgetClientCtrl($scope, $compile, $element) {
                    var self = this;
                    self.$onInit = function () {
                          renderWidget(self.name, self.payload);
                    };
                    function renderWidget(name, payload) {
                          var template = '<' + name;

                          if (payload) {
                             $scope.payload = payload;
                             template += ' payload="payload"';
                          }

                          template += '></' + name + '>';
                          $element.append($compile(template)($scope));
                    }
        },
        bindings: {
            name: '@',
            payload: '=?'
        }
    });

your dynamic component:

app.component('someDynamicComponent', {
    templateUrl: 'yourPath',
    controller: dynamicComponentCtrl,
    controllerAs: 'vm',
    bindings: {
        payload: '<'
    }
});

and then:

<li ng-repeat="(name, payload) in vm.dynamicComponents">
      <dynamic-wrapper name="{{name}}" payload="payload"></dynamic-wrapper>
</li>
查看更多
登录 后发表回答