How can I pass around domNodes with bindings to other controllers/directives? - javascript

I'm really new to Angular, but learning quickly. However, the one problem that I haven't been able to find an answer for yet is how I can pass around HTML snippets with bindings attached.
e.g. With jQuery I could do something like
var $div = $('<div id="test"><button>CLICK ME!</button></div>');
$div.delegate('button', 'click', function () { alert('CLICKED') });
Then I could pass around this $div variable to other objects. For instance I would use this pattern to separate page specific content from the code of a modal Singleton that encapsulated general functionality.
e.g.
var name = "The Dude";
var $div = $('<div id="test"><button>CLICK ME!</button></div>');
$div.delegate('button', 'click', function () { alert('Hi, ' + name) });
Modal.open({ content: $div});
How can I achieve something similar with Angular?

You should not pass around template code in the controllers, that's not the "angular way". If you have to do DOM manipulation it should stay in a directive. A lot of jQuery developers fall into this trap of thinking like you do with jQuery. But anyways here's how I would solve this problem:
I'm writing this in a directive so it's more modular. You could move this into a controller and it'll work just fine.
Code in plnkr here
In the directive you have a template which contains your code and you can have a ng-click binding which can control a click action to open the modal.
Now as far as the content in the modal is concerned, making the actual template code modular so you can decouple the content from the template depends on if/what angular plugins you're using. They all do something similar but the names may be a little different. For example, if you're using ui-bootstrap(which is awesome by the way, if you dont use any plugins I'd highly recommend it) there's a resolve function that takes a variable as an argument and you can display that in the view. That's exactly what you want to send custom content to the modal(eg. name)

Related

Cannot use css() on dynamically created div in custom plugin

I have recently taken up learning how to make a jQuery plugin. This is my first attempt at just creating something that is very simple. I am still relatively new to jQuery and have come into a bit of a bind when it comes to selecting dynamically created content. In this scenario I am attempting to select a div I created within the plugin.
I have made a jsFiddle here.
I have perused many posts about selecting dynamically created div's and most of them are solved either using on or a callback function. And I am not sure that those can be applied in this situation.
I think the issue occurs at this point in the code:
$element.append("<div class=\"gifLoader\"></div>");
$gifLoader = $element.find('.gifLoader');
$gifLoader.css("bacground-image", "url(\"" + plugin.settings.gifSrc + "\")");
plugin.settings.callback.call(this);
Is there some way I can use a callback function like you would with methods like fadeTo? Also, if anyone cares to comment. I would really appreciate some feedback on the layout of my plugins. I don't fully comprehend what it is I am doing when making a plugin, I am just hoping to learn how to use Javascript and jQuery without the coding looking so clunky. (Before I just had anonymous functions within anonymous functions)
You have one typo, and one error: bacground-image => background-image, and src() => url()
Change
$gifLoader.css("bacground-image", "src(\"" + plugin.settings.gifSrc + "\")");
To
$gifLoader.css("background-image", "url(\"" + plugin.settings.gifSrc + "\")");
When you are creating a dynamic element using javascript or jquery, that element does not reflect in your DOM straight away. If you take a look into the HTML that is generated using the developer tools you'd find before: and after: tags appended with your HTML.
The best way to realize the problem is to either specify the background-image when you are creating the div like:
$("#element").append("<div class='gifLoader' style='background-image: url('" + plugin.settings.gifSrc + "')'></div>");
Otherwise if you are looking to change the background-image on some kind of user interaction, you can go for this:
$("#element").append("<div class='gifLoader' id='someID' someEvent='transform(someID)'></div>");
And then have function transform() defined somewhat like:
function transform(elementID) {
$("#" + elementID).css("background-image", "some image URL");
}

Angularjs: is this the right place to use a directive?

I've recently switched from jQuery to Angularjs and I am in the process of re-coding some pagination logic for the links ("Next", "Previous", etc.) that were written in jQuery-style Javascript previously.
Each link has an ngIf condition (for example, the "Previous" link won't show if you're on page 1) plus an ngClick event, which essentially updates a scope variable called $scope.pagination.position that determines which results are displayed in the table.
My original code was something like this (simplified for clarity):
Template
<a ng-if="pagination.position > 0" ng-click="pagination.first()">First</a>
Controller
$scope.pagination = {
first: function() {
this.position = 0;
}
}
Then I learned more about directives, and how most DOM elements that aren't static HTML should be created using a directive. So I switched each link (since each has it's own display rules and behaviour on clicks) to its own directive, like so:
Template
<a pagination-first></a>
Directive
app.directive('paginationFirst', function() {
return {
link: function(scope,el,attr) {
scope.pagination.first = function() {
scope.pagination.position = 0;
}
},
replace: true,
template: '<a pagination-first ng-if="pagination.position > 0" ng-click="pagination.first()">First</a>'
}
});
I'll cut straight to the chase : am I doing directives wrong? All that's happened, from my perspective, is I've flipped from having logic in my template to having a template in my logic, and I've defined the click event function in the directive rather than in the controller.
Is this even an appropriate time to be using a directive?
I'd like to learn best practices, so I'd love to know if I've missed the point and if the original templated-based ngIf and controller function approach was fine, even with longer and more complex ngIf conditions than the one shown.
If I want to add specific behaviors to a dom or dom list then I normally create a directive. As per angular js perspective the dom manipulation should only be done through directive (For me it is the best place, sometime I have to disobey this due to my lack of knowledge ). I specially found directive use full while creating a widget. In one of my project there was a part where a section is dedicated to display an image and also upload the image. I just use the directive on the top div, with the help of link function I attached the event handlers to various child dom. And as my project doesnot require an isolated scope (as this widget was all used in a single project and the outer scope was under my control) . So it worked like a charm. I cerarted the directive once. And used that widget through rest of the project as it's behavior and design (of the widget ) was same through out the project. For the pagination widget you can create a directive. Take the directive attibutes value as the input of the pagination parameters. Like calling script, limit offset. Container identifier to update the content. Then you can solely concentrate on the pagianation behavior. But from my experience (as I am also not so experienced in angular js), sometimes it becomes a little hectic to develop a directive and and use that throughout the project. As in some places we need to modify the behavior of the directive. And for this it may breaks elsewhere. But I know as I learn more I will be more efficient to handle this kind of situation. Hope my experience will help you.

Dynamic views based on content in an Ember.js CMS

I'm very new to both Ember and web development. Let's say that I'm building a site that displays news articles. I'd like some interaction with Wikipedia links in each article so that when you click on them, a little box will pop up in place with the summary (like the Instapaper app). I'd also like some interaction within the box, expanding and collapsing the summary, etc. What is the most Ember-like way to do this?
I think that with jQuery it would be trivial to add a click handler to the links to add the box to the DOM, but I'd really like the box to be an Ember view. I thought I might have been able to create a view dynamically and append it to a DOM element but it seems that it isn't allowed, and you can only append to ContainerViews.
How should I approach this?
Update on what I've tried
Essentially, I have this as a template for each article so there are paragraphs and a container on the right of each paragraph for the boxes:
{{#each paragraph}}
<div class='paragraph-container'>
<p {{bind-attr id=paragraph_id}}>{{html_text}}</p>
<div class='paragraph-reference-container' {{bind-attr id=paragraph_reference_id}}></div>
</div>
{{/each}}
And an associated view. paragraph_reference_id and paragraph_id are computed properties so I can set the IDs properly here. I also have a ReferenceView for the boxes, which is uninteresting since it's just placeholder content and code at the moment. I was racking my brain trying to figure this out, and ended up trying something like this to append the view as was in the documentation:
App.ArticleView = Ember.View.extend({
templateName: 'article',
classNames: ['articles'],
didInsertElement: function () {
var container = this.container;
Ember.run.scheduleOnce('afterRender', this, function () {
var em = this;
em.$('.paragraph-container').each(function () {
var referenceContainerID = $('.paragraph-reference-container', this).first().prop('id');
em.$('a.wikipedia-link', this).click(function () {
var referenceView = container.lookup('view:reference');
referenceView.appendTo('#' + referenceContainerID);
});
});
});
}
});
But I got the "Uncaught Error: Assertion Failed: You cannot append to an existing Ember.View. Consider using Ember.ContainerView instead." error. Apparently this is no longer supported as mentioned above. I don't really know what the "Ember" way is to do this and still use views (which I'd really like to do) instead of plain HTML and jQuery.

AngularJS good practice : Hide/show new DOM elements or compile

I'm new to AngularJS and I want to change the behavior of a button dynamically.
As I am used to JQuery, I use element.attr('ng-click', 'controller.function()');
But, as I understood AngularJS needs to compile the new DOM.
But, in my head "compile" = "cost".
What is the best practice :
Change/Add DOM elements then compile when needed.
Hide another button, and just show/hide the one I need.
Thank you !
I prefer to use ng-switch for these type of things. It ends up being much simpler and its always a good idea to make use of the existing angular directives where possible. Other developers working on your project will appreciate the simplicity.
http://jsfiddle.net/L9T6J/2/
the html...
<div ng-switch="state">
<button ng-switch-when="0" ng-click="updateState()">Label 1</button>
<button ng-switch-when="1" ng-click="updateState()">Label 2</button>
</div>
and the javascript...
$scope.state = 0;
$scope.updateState = function () {
$scope.state = $scope.state === 0 ? 1 : 0;
}
i generally hand that type of thing in angular like so
markup
<button ng-click="myButton.actions[myButton.state]()">
{{myButton.labels[myButton.state]}}
</button>
scope
$scope.myButton = {
state: 0,
labels: ['label 1','label 2'],
actions: [
function() {$scope.myButton.state = 1;},
function() {$scope.myButton.state = 0;}
]
};
here is a fiddle to see it in action
http://jsfiddle.net/pixelchemist/L9T6J/
It depends entirely on the application and scope of the directive, I go either two ways:
If it's between different buttons (if-elseif-else etc), the directive is very simple and explanatory by just looking at it, I use ng-if or ng-switch.
If it's a complex directive that lives on its own, meaning it has its own scope and functions, there's no reason for it to be cluttering the DOM. For instance, if you search through the DOM a lot, you're going to be adding more data to search through, then instead if you destroy and rebuild it only when its used. It also clutters the DOM if you have tons of custom directives that you show/hide.
If your application has several heavy directives that contain complexity and you only use them after you click on a button, it makes more sense to create/destroy, than to hide/show the directive (helps if the directive contains data that it is getting from a service).
Also it just feels more right to invoke sub applications when you need them, rather than hiding/showing them, though this is a personal preference and I might be wrong.
The majority of custom directives you use from 3rd parties, they create/destroy their directive rather than show/hide.
Not entirely sure how the $watchers work with custom directive when hidden, but you removed that possibility when you create/destroy directives which helps with performance. For instance, if you render your HTML often, then $watchers pile up if you have a lot of ng-if/ng-switch, on the contrary, if you create/destroy, then when the html DOM is destroyed, Angularjs won't check the $watcher because it doesn't exist so you gain performance.

Javascript - Execute HTML/Angular ng-include

I'm using AngularJS to design a small app for searching. I have a div that's currently empty, and after running a function, for it to replace it with a div that has ng-include. The div replaces just fine, but ng-include doesn't load up.
I'm currently running the following in console for testing to see get it running. How would I go about getting this to work? Any help is appreciated!
document.getElementById("resultsDiv").innerHTML = '<div ng-include="" src=" \'sr.html\' "></div>';
read about angularjs $compile function
I don't know why you need to make this JavaScript-call, but its definitely no the 'angular-way'. If you need to conditionally include html, i would recommend using ng-if, ng-hide, ng-show or even ng-switch.
You could do something like this:
// somewhere in your controller
$scope.triggerSomeInclude = true;
// in your template
<div ng-if="triggerSomeInclude">
<div ng-include ="'someFile.html'"></div>
</div>
Another approach would be using a directive. They are the only place, where selecting an element directly could make sense (although even there it usually doesn't to select an element via id). But as I said, it's hard to stay what the best method would be, as I'm not sure what you're trying to achieve.
Although you're not using jQuery, what you're trying to do looks very jQueryesque (awful word) as you're selecting an element directly seemingly totally detached from the $digest and $compile-cycles of angular, so I also would recommend to read this answer:
"Thinking in AngularJS" if I have a jQuery background?
In the end, the method I used was templating example used for ngInclude.
http://docs.angularjs.org/api/ng.directive:ngInclude
Inside my controller, for example, by default it would set the following:
$scope.mainDivTemplate = {url: 'partials/test1.html'}
If I wanted to switch it to something else, I'd call a function that would look a little something like this.
$scope.loadHome = ->
$scope.mainDivTemplate = {url: 'partials/home.html'}
$scope.$apply()

Categories