Angular directive and dynamic templates: where does the controller logic go? - angularjs

Here's the situation: I have entities that have many different properties: scalars, arrays, references to other entities, etc. Each property type can be displayed differently depending on the action (create, edit property definition, edit property value, view). Each property is treated equally by the entity and is usually rendered by iterating array of properties and creating a generic property directive.
The property directive checks property type and desired action in linking function, fetches the appriopriate template and compiles it. Here's some pseudo-code:
myModule.directives('property', function() {
return function(scope, element, attributes) {
var template = fetchTemplate(scope.propertyType, attributes.action);
element.html($compile(template)(scope));
}
}
Where should I put the controller logic? Currently I define a dedicated controller for each property in the template via ng-controller and put that controller to the application's controllers file. Yet I feel the logic should go into the directive itself. Here's the catch: I cannot use directive's controller function as the template is not ready by then. Should I handle all the logic for different property types in the same link function that fetches template? Or should I define directives for each property and then declare those directives in property templates? (This one seems doesn't seem right at all because a directive loads a template that declares another directive to do controller stuff).
What is the angular way of achieving this with dynamic templates?

Related

Angular scope modification inside transcluded content

Angular: Can anyone explain why transcluded content in a directive can only update objects on the scope - not variables directly on the scope. Is it just because the object and functions are ref type in javascript and why does the binding work one way and ... why does the binding break after the update inside the transcluded content (see plunker samples)
-Plunker sample - variable on scope vs object on scope
Working -Plunker sample - variable on scope
Transcluded content can also update parent's scope properties
Transcluded content is like any other content, therefore if you followed the dot.rule you'll be able to update the parent scope properties you want. Always follow the dot.rule and refactor your logic to make sure everything is done in the angular way.
Directive scope types
Directives in angular prior to 2.0 version accept several types of scopes, the scope can be true, which creates a new one and inherits parent's properties; false, which does not create a new scope, but still inherit parent's properties; or {} which is known as an isolated scope, this creates a new scope with zero properties, it keeps only the properties you declare.
One-Way vs Two-Way data binding
Angular uses both, one-way and two-way data binding. For example, two-way data binding occurs when you use the ng-model directive, whenever you update the model, the view will reflect those changes and viceversa. On the other hand, one-way data binding occurs when you use the interpolation {{some.property}}
The two-way data binding should not break if you are using the dot.rule. That's how prototypical inheritance works after all.
Check out this Pen to illustrate everything said in this answer.

Any way to inject styling into an AngularJS template in a contributed directive

I am using a contributed directive that contains a template. I'd like to modify that template to use a function to set styles on specific elements in the directive template via ng-style (i.e. conditionally set a background color based upon a value within said directive).
Now, I can modify the template to contain the ng-style directives easily just overriding the template in $templateCache. However, given that the directive has its own isolate scope, I don't know what function I could call that I can define without modify the directive definition.
Is there a way to either a) inject a function into the directive that I can use in my overridden ng-style-containing template or b) write my ng-style directives in such a way that they can access the containing (or global) $scope?

Angularjs directive + do I need to $compile

I'm in need of some pointers in my landingPage-builder project. (i'm currently stuck!).
The main issue is as follows:
Each element in the template (like the h1 and the paragraph) has attached a directive. What I need to get the directive to do is: create a template of HTML with some other directives attached like ng-click, ng-options etc, keep the bindings to the model intact (currently far away from working), update the model when changed.
I'm not trying to append to, or replace the element the directive is on, but make a html-template and inserting it into the DOM (almost like another view) so that the model on the left can be updated from the "settings" box on the right.
The project can be viewed here: http://193.107.29.196/~stian123/landingPageV3/app/#/pagebuilder/2
You may need Allow-Control-Allow-Origin for Chrome: https://chrome.google.com/webstore/detail/allow-control-allow-origi/nlfbmbojpeacfghkpbjhddihlkkiljbi/related
I'm a bit confused about $compile and doesn't really know when I need to use this part of the directives api.
Any suggestions?
Thank you!
If I understood your question correctly, you want to dynamically create templates, some of which have Angular attributes in them, then attach them to the DOM.
First, to (hopefully) answer your question, about when to call $compile:
Whenever you load in HTML from outside Angular's template system (like trying to set $(element).html(myHtmlString)), you need to let Angular compile it before you attach it to the DOM. In other words:
elem.append($compile(yourHTMLString)(scope));
This lets Angular traverse the DOM and parse any directives and bindings and attach them to the provided scope. If you don't $compile, Angular has no idea about those intended bindings at all, the HTML is never read by Angular.
Second, I don't know how flexible you want your templates to be, but if they're relatively fixed, but with some fixed customizable options (text, color, font-size etc), you might be better off creating a directive for each 'view', with the view options bound to the scope of the directive. Then you can just change the fields on the scope of the directive in the panel on the right side, and the view will update directly. You wouldn't even have to use $compile in this case.
If you want the user to be able to manually add the template HTML code, you will have to compile the HTML as described above.

Building a reusable AngularJS directive with two-way databinding set through code

I'm trying to build a reusable directive that shows a dialog box when a button is clicked and allows the user to customize an array of strings. I have this working fine in a single-use-case scenario, but I'm trying to figure out if I can create two-way data binding through code, so that I can use a single instance of this directive multiple times with different arrays of strings.
In my HTML, I have the directive (note the lack of binding to a particular array):
<my-array-dialog control='arrayDialog'></my-array-dialog>
I'm exposing a shared control object with a showDialog method on it that causes the dialog to be shown. An example of that pattern is: http://plnkr.co/edit/MqN9yS8R5dnqTfjqldwX?p=preview
What I want to do is have two-way data-binding with the parent controller passing in the data into my showDialog method. I'm unsure how to configure this though, or even if it is possible.
In my directive, I have the following:
$scope.control = {
showDialog: function (arrayData) {
// Ideally, this would create two-way data binding
$scope.arrayData = arrayData;
// Manipulate the DOM here to show the dialog
}
}
Ideally, changes to $scope.arrayData would be reflected in the calling code's arrayData. Again, the reason I'm not setting this up as an attribute is because I want to call this dialog multiple times with different data and to avoid having multiple dialog directive instances.
Is it possible to set up the two-way data binding in this manner, or am I going about this all wrong?
Absolutely! Check out the intimidating yet incredibly helpful AngularJS docs page on $compile. In particular, what it sounds like you'd like to do is create a two-way binding between a parent scope variable and a variable in the local scope of the directive.
When defining a directive, you can specify a scope object. You'll want to create an isolate scope (for reusability) and use the # feature to create the two-way bindings with the HTML attributes.
From the $compile docs:
= or =attr - set up bi-directional binding between a local scope property and the parent scope property of name defined via the value of the attr attribute. If no attr name is specified then the attribute name is assumed to be the same as the local name. Given and widget definition of scope: { localModel:'=myAttr' }, then widget scope property localModel will reflect the value of parentModel on the parent scope. Any changes to parentModel will be reflected in localModel and any changes in localModel will reflect in parentModel.

dynamically load interfaces, using angularJS: 2-way binding breaks

I'm trying to build web app that dynamically load interfaces, using angularJS.
I found that it was possible to bootstrap some portions of my code after the initial bootstrap of Angular (HTML template + Controller).
My problem is that, doing so, the 2-way data-binding doesn't work. See for yourself:
http://plnkr.co/edit/MtAWP6
Any idea? Am I seeking for something to do the wrong way?
Thanks!
Your problem isn't a bootstraping one (although you really shouldn't be using bootstrap to instantiate a controller, but rather $compile, imo - see this answer). It is a scope problem. You define a "mymodel" model in your controller, but then define it again in your form, for which angular automatically creates it's own scope. While the form's scope inherits from the parent scope, and thus seems to be "binding" the model, the inverse doesn't happen.
You need to either establish a binding between both scopes (or $watch the form's variable, or define the for in the surronding controller), or just assign the controller you want to the form, directly.
See your problem exposed here (see that while your $timeout changes both models, manually setting the model only changes one)
See it resolved here (by basically assigning your controller to the generated form, rather than to a enclosing div of said form)
I think maybe you should take another look at routing/ deep linking. You should be able to specify both a template url and a controller.
Check out this video
And the api docs

Resources