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.
Related
Some (advanced?) angular is making my head hurt.
The goal is to extend a WYSIWYG HTML editor to allow users to insert certain angular directives into arbitrary HTML content. I have chosen medium-editor and its angular-medium-editor wrapper (but I'm not wedded to that if there are better solutions).
This Plunk shows how the editor directive is instantiated and activated (using an editable attribute). The toolbar is customised to include a button which adds a custom directive around selected text: <my-custom-directive class="bg-info"> ... </my-custom-directive>. (For demonstration, the custom directive wraps (transcludes) its contents in a button which triggers an alert when clicked).
I'm having problems with (re-)compiling the editor's content so that the directives inside the editor compile. Using $compile(element.contents())(scope) throws ngTransclusion:orphan errors for directives which uses transclusion. (I understand this is due to angular already having made the transclusion by the time the editor's link function is called.)
I cannot refactor all potential custom directives to not use transclusion.
What pattern can I use to successfully compile arbitrary editor content (which may include many different directives), ideally whenever that content changes, or at least when the editing is finished? Is this one of the "fringe cases" where the use of $compile is justified? If so, how do I use it?
This question and answer made me realise that the way to do this is to $compile only the inserted element when it is inserted, rather than recompiling the whole section.
Handily, rangy's classApplier module allows for an onElementCreate callback which can be used to compile the custom directive as it is added.
Here's the working plunker.
I'm working with angular js and there's one thing I didn't fully understand yet. I know what directives are: they are "additional features" that we add to HTML that can be used as elements, attributes, comments or classes and that can change completely the markup there by some other thing rendered by angular or can add functionality with the link function.
That's fine, but when to use directives? I know that if we want to represent on screen some domain specific object then this is one possible candidate for a directive, or when we want to add functionality to some HTML element (like slider functionality to an input) then we use a directive. But what about other cases? What about when we want to manipulate the DOM to, for instance, activate the functionality of a sidebar or thing like that? Directives are used for this to?
How do when know when to use a directive in angular?
I think about directives when I face one of this two scenarios:
Custom control: I need to implement a custom control, that probably I will reuse in other parts of my app or even other projects.
Custom Validations: AngularJS provides a limited set of validations (e.g. ngRequired, RegEx...), but for sure you will need to implement your custom logic validations. I prefer to implement that validations in directives (reuse, SRP, easy to be tested isolated) rather to overload the controller with that logic.
Some directive rules of thumbs:
If you plan on adding the same markup-based functionality more than once, create a directive (but if it's simple enough, just use ng-include).
If you need to modify the way an ng-modeled input field validates, formats, or parses, create a directive that requires ngModel.
If you want to make writing unit tests easier for a specific piece of markup, write a directive.
If you come from a jQuery background and instinctively feel like using $('.jquery-style-selectors') from your controller, instead write a group of directives where the child directive(s) require the parent directive(s) and communicate via the directive controller(s).
I'm currently working on a new custom directive which will transclude some HTML around the element you've called it on. This means however that if you have a ng-show directive as well on the element that the HTML would still be transcluded and hence shown.
A working example of the directive is located on this Plunk.
I'd like to counter this, by making my custom directive respond to ng-show but I can see a big problem with this approach as ng-show will hide or show the whole element it is called on.
On the other hand, I don't really like having a lot of custom directives each defining their own visible properties as an alternative to ng-show.
A third option would be to support both? This would allow me to switch of specific directives on an element as well as hiding it completely with ng-show
Has anyone got good recommendations on which approach I would have to take here? This is not a solitary case, we have a lot of custom directives of which we'll need to control visibility.
To summarize, these are the three options I'm thinking of:
Let custom directive respond to ng-show
Define own visibility control per custom directive (which could use ng-show underlying)
Support option 1 and 3
Any insights greatly appreciated.
I need to build a dialog to be used with any item on a list of items. The dialog is pretty much the same regardless of the item except for the values of the fields which are obviously item dependent.
The directive I am building is reading the template from a file, compiles it with $compile and then binds (links) it to the scope of the item. The result of the binding is a DOM tree. To make the dialog visible I need to append this tree to some element in the existing DOM. The nature of my dialog is such that it makes sense to append it directly to the body tag. The dialog will be used many times in combination with different items on the list
So here is my question: How much of this process (compile, bind, append) can be done in advance? I certainly can run compile once. I can also bind the compilation result to the $rootscope and append (hidden) it to the body tag. This way I can later just turn on visibility and show the dialog.
But if it is already bound and attached to DOM, is it kosher to re-bind it to some other scope, if so - what's the right way to do it? Another question is is it even worth it? might be just re-insert it every time it is needed?
If you're only ever going to display one dialog like that at a time and you will use it frequently, you don't have to re-bind it to another scope, just change the data on the scope. Something like this:
Create a service for your dialog
Create the directive and inject your service into it. When the linking function executes, pass something like $scope.dialogData to the service so that the service can update the data.
Create a controller that gets the service injected. Set the dialog data through the service to display the dialog. Since you're modifying data in your controller that's on the directives scope, Angular notices that and updates your dialog.
Add ng-show on your dialogs wrapper to make it simple to implement open()/close() methods on your service.
Now you have a dialog that can be used from anywhere in your system, and you're just re-using the same directive without having to mess with the DOM or compilation.
This is indeed excellent question and I'm happy to see that more and more people are starting to approach dialogs as services.
Regarding your particular questions, here are some of my thoughts:
You can "cache" linking function (that is - function that is returned from the $compile call) and then call this function as needed (passing in scope variables).
Instead of inserting (hidden) compiled element you could only attach it on demand, when a dialog gets opened. On top of this I would rather attach modal element to the $rootElement instead of <body> just not to touch DOM elements above where ng-app was defined. Just not to touch parts of the DOM that AngularJS is not controlling.
IMO dialogs are really close to AngularJS routes (as they provide different "views") and as such it would be very nice to have ability to resolve promises before modal is shown (as with routes).
In fact there are number of things to consider when designing a good, generic dialog service and I hope that those advice, alongside with excellent input provided by others, will get you started. But this all is a bit theoretical so if you are looking at the implementation of what was discussed here you can have a look at this implementation. ($dialog service from http://angular-ui.github.com/bootstrap/ - it is fully customizable so can be used with CSS other than Bootstrap's. Documentation here).
It can be seen in action in this plunk: http://plnkr.co/edit/PG0iHG?p=preview
Excellent question I think. You're wondering if one can "hot swap" the scope of an element. I don't know if there's a way to do that, or even if there is, if that's the Angular way. I take it you looked at how ng-view works to get as far as you've gotten?
My advice is to do the $compile once, keep the result, the link or transclusion function or whatever it's called in Angular parlance, around somewhere. And call it for each needed instance of the dialog.
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