Using Skobbler in AngularJS directive: 'Map container is already initialized.' - angularjs

Using a directive to render the Skobbler leaflet:
angular.module('app.directives').directive('skm', function()
{
return {
restrict: 'E',
replace: true,
scope: false,
template: '<div></div>',
link: function(scope, element, attrs) {
var map = L.skobbler.map('skm', scope.skm);
// ....
}
}
});
This directive is used on different routes/partials in a similar way:
<skm id="skm"></skm>
Version info:
Skobbler 2.0
AngularJS 1.2.16
When moving from page 1 to page 2, no problem. However, going back to page 1:
Error: Map container is already initialized.
I've tried storing the map object and using map.remove(), which removes the error but stops the map from rendering.
FIXED: See my answer below. (Use unique ID's in each partial and grab those)

Fixed: Simply put a unique ID in each partial and grab the ID in the directive, then create the leaflet with this ID.
Partial 1:
<skm id="skm1"></skm>
Partial 2:
<skm id="skm2"></skm>
Directive:
angular.module('app.directives').directive('skm', function()
{
return {
restrict: 'E',
replace: true,
scope: false,
template: '<div></div>',
link: function(scope, element, attrs) {
var map = L.skobbler.map(attrs.id, scope.skm);
// ....
}
}
});
What didn't work:
replacing the outerHTML with original HTML on directive $destroy (directive didn't play well)
map.remove() (Skobbler didn't care)
Shouting profanities at the Skobbler library (Skobbler didn't care)

Skobblerjs is a "thin" fork of leafletjs so (most likely) the error comes from the leaflet logic and not the skobbler logic.
There are some other mentions of this error, see of any of following are of help to you:
https://groups.google.com/forum/#!topic/leaflet-js/xDNcNBAZq8o
http://forums.enyojs.com/discussion/540/leaflet-maps
If not - then include a jsfiddle or plunker example so that we're able to debug

Related

Simple AngularJS directive with attributes is not working

I have created simple directive with a few parameters passed, but my parameters are not passed to Template from Usage code. (they are not displayed on page, no error is displayed too). I also used '#' instead of '=' but there was no real improvement.
I just want to display my attribute parameters in my directive code:).
Thanks for help.
Usage code:
<fine-faulty-directive fineCount='111' finePercentage='11'
faultCount='222' faultPercentage='11'></fine-faulty-directive>
Directive code:
Application.Directives.directive('fineFaultyDirective', function () {
return {
restrict: 'E',
templateUrl: 'src/Dashboard/Views/fine_faulty.html',
//replace: true,
scope: {
fineCount: '=',
finePercentage: '=',
fauntCount: '=',
faultPercentage: '='
},
link: function (scope, elem, attrs) {
console.log(scope);
}
};
});
Simple templateURL:
{{faultPercentage}}% - {{faultCount}}
The attributes fineCount, finePercentage, faultCount, faultPercentage are normalized names (such as directive's name), so if you want to set it to the element, each attribute name must be fine-count, fine-percentage, fault-count, fault-percentage...
I hope I helped.
Because of
<fine-faulty-directive fineCount='111' finePercentage='11'
faultCount='222' faultPercentage='11'></fine-faulty-directive>
AngularJS will translate finePercentage to finepercentage and fineCount to finecount
to avoid such circumstances, use fine-percentage instead of finePercentage
and fine-count instead of fineCount
NOTE: all attribute which are camel cases in attribute should be separated with - or : so that you can get them camel case in your JS code.

Angular: How to get content from a custom div

I want to get a the content from a custom div tag, I tried various ways to do it, not working well. Here is an example. The general item is to retrieve the content in the custom directive tags. and then bind them into the template. I hope some one can give me a suggestion or solution that does it, or does similar things
The html
<questions>
<qTitle> this is title</q-title>
<qContent> this is content <q-content>
</questions>
The angular js
var app = angular.module('app'[]);
app.directive('questions', function () {
return {
transclude: true;
template: "<div class='someCSSForTitle'>{{qTitle}}</div>"+
"<div class='someCSSForContent'>{{qContent}}</div>"
link:(scope, element, attrs)
scope.qTitle = element.find(qTitle).innerHTML
scope.qContent = element.find(qContent).innerHTML
}
}
});
First I'd advise you to read the AngularJS Guide. You didn't even copy-paste the structure correctly and you have javascript and even html errors.
Basic fixes:
HTML
<questions>
<q-title>this is title</q-title>
<q-content>this is content</q-content>
</questions>
Why do you mix qTitle and q-title?
As regarding JS:
app.directive('questions', function () {
return {
restrict: 'E',
replace: true,
template: "<div class='question'>{{title}}</div>", /* simplified */
link: function(scope, element, attrs) {
scope.title = "hallo";
console.log(element.html());
}
};
});
by default, restrict is set to 'A'. That means attributes. Your syntax is for elements.
replace set to true is not compulsory. However, because the browser doesn't understand your elements but does understand the content ("this is title"), it will print it.
the link function has to be a function. you had syntax errors there (same for transclude: you had something that you were not using followed by ";")
You can print the element to know the contents. If you do, you'll see that element in link is not question.
Now if you want to read the content, you can use a transclude function or create directives for each part and create the template separately. This seems simpler. Live example:
app.directive('questions', function () {
return {
restrict: 'E',
transclude: true,
replace: true,
template: "<div class='question' ng-transclude></div>",
};
});
app.directive('qTitle', function () {
return {
restrict: 'E',
transclude: true,
replace: true,
template: "<div class='title' ng-transclude></div>",
};
});
In this case you translude the contents to an inner div.
You can also define custom complex transclude functions in the compile phase but this doesn't seem necessary here.

Error: No controller in directive

Folks,
I am an AngularJS newbie and I am trying to create a basic HTML hierarchy bound to a model hierarchy using Angular. My top-level module looks like this:
angular.module('fooApp', ['ngResource', 'myList']) which is declared as ng-app="fooApp" at the root div for my page.
I then have HTML that looks like this in my page:
<my-item-list/>
I have a second module that looks like this:
var myList = angular.module('myList', []);
myList.directive('myItemList', function factory() {
var directiveDefinitionObject = {
priority: 0,
templateUrl: '/assets/partials/my-item-list.html',
replace: false,
transclude: false,
restrict: 'E',
scope: false,
controller: function($scope, $element, $attrs, $transclude) {
// No Op
$scope.foo = 'bar';
},
compile: function compile(tElement, tAttrs, transclude) {
return {
pre: function preLink(scope, iElement, iAttrs, controller) {
// No Op
},
post: function postLink(scope, iElement, iAttrs, controller) {
// No OP
}
}
},
link: function postLink(scope, iElement, iAttrs) {
// No Op
}
};
return directiveDefinitionObject;
});
The partial HTML template just contains a table and an ng-repeat on a tag. The template renders just fine, but my JavaScript console contains :
Error: No controller: myItemList
at Error (<anonymous>)
at h (http://localhost:9000/assets/javascripts/angular.min.js:41:458)
at i (http://localhost:9000/assets/javascripts/angular.min.js:43:415)
at http://localhost:9000/assets/javascripts/angular.min.js:48:191
at http://localhost:9000/assets/javascripts/angular.min.js:94:307
at h (http://localhost:9000/assets/javascripts/angular.min.js:78:33)
at http://localhost:9000/assets/javascripts/angular.min.js:78:266
at Object.e.$eval (http://localhost:9000/assets/javascripts/angular.min.js:88:347)
at Object.e.$digest (http://localhost:9000/assets/javascripts/angular.min.js:86:198)
at Object.e.$apply (http://localhost:9000/assets/javascripts/angular.min.js:88:506) <my-item-list>
I intend for the nesting to increase (e.g. replace the in the HTML partial template with another directive and I really need to figure out what I'm doing wrong. It feels like I'm doing some kind of newbie mistake. Whenever I add a 'require' attribute to my directive definition, I get the same "no controller" error message, but with the name of whatever I required (even if I try and require ngRepeat or the page's master controller).
It looks like a bug in Angular: http://github.com/angular/angular.js/issues/1903
Does your directive really need a controller? I would say 9 times out of 10 directives do not need their own controller (mostly they just need link).
When you remove controller from the directive, the error appears to go away.
Edit: More specifically, it appears that the problem arises when you combine controller and compile. Any other combination (controller+link, compile+link, or any by themselves) works fine.
Found a solution for this...
Directives should be nested, example,
<directive-parent>
<directive-child></directive-child>
</directive-parent>
Check it our here:
https://groups.google.com/d/msg/angular/SRKL0wZtSew/6sToIKLkRHQJ

angularjs directive with dynamic templates and string interpolation

The idea is to replace the directive element with the dynamic template which refers to interpolated strings.
If I use element.html() in my directive then the strings are interpolated fine but this leaves the original custom directive html element.
If I use element.replaceWith() then strings are not interpolated. I guess it has related to scope but can't figure out what's wrong.
Plunker: http://plnkr.co/edit/HyBP9d?p=preview
UPDATE
Found the solution. Using element.replaceWith($compile(html)(scope)); works.
Updated plunker: http://plnkr.co/edit/HyBP9d?p=preview
Not sure what objective is regarding replaceWIth. The problem is likely that when you replace an elememnt, you replace all events and data bound to it. This would include the angular scope for the element.
For demo provided could do it like this:
app.directive('status', function($compile) {
var linker = function(scope, element, attrs) {
element.contents().wrap('<h'+attrs.value+'>')
};
return {
restrict: 'E',
replace: true,
template:'<div>{{value}}</div>',
transclude: true,
link: linker,
scope: {
value: '='
}
};
});
DEMO:http://plnkr.co/edit/QDxIwE?p=preview

Gridster jQuery plugin on AngularJS ng-repeat content going bad

I'm trying to integrate Gridster with AngularJS, but without too much success yet.
Reading the documentation on Angular UI's ui-jq directive, I get the impression that this (check fiddle) should work. But when I look a bit further with Chrome's debugger, it turns out that on this line, it doesn't find any children at all.
I suspect that somewhere in the ng-repeat directive, AngularJS decides to rip out the part that will be repeated, and I see why, but that doesn't solve my problem. I'd welcome any clue that would help me to get a little further.
Update 1
I started turning it into a directive, hoping that would improve things. However, the nested ng-repeat is also getting in the way in case of a homegrown directive. I tried postponing hooking up the jQuery plugin as long as I could ($evalAsync) and alike, and eventually ended up using a $timeout. That's the only way in which I could get it working.
Update 2
I think the original approach would have never given me what I needed. So implemented a custom directive. See my answer below.
I eventually ended up writing my own directives for it. I needed to be sure that every change to the underlying data would be seen by gridster, but at the same time, I didn't want to write my own monitoring on the data model and replace everything you normally do within gridster with a directive that hides all of that. (It would involve implementing most of ng-repeat within the directive itself.)
This is what I have (and assume "foo" to be the name of my module):
foo.directive "gridster", () -> {
restrict: "E"
template: '<div class="gridster"><div ng-transclude/></div>'
transclude: true
replace: true
controller: () ->
gr = null
return {
init: (elem) ->
ul = elem.find("ul")
gr = ul.gridster().data('gridster')
return
addItem: (elm) ->
gr.add_widget elm
return
removeItem: (elm) ->
gr.remove_widget elm
return
}
link: (scope, elem, attrs, controller) ->
controller.init elem
}
foo.directive "gridsterItem", () -> {
restrict: "A"
require: "^gridster"
link: (scope, elm, attrs, controller) ->
controller.addItem elm
elm.bind "$destroy", () ->
controller.removeItem elm
return
}
With this, I can have a gridster generated view, by adding this:
<gridster>
<ul>
<li ... ng-repeat="item in ..." gridster-item>
<!-- Have something here for displaying that item. -->
<!-- In my case, I'm switching here based on some item properties. -->
</li>
</ul>
</gridster>
Whenever items are added to or removed from the collection observed by the ng-repeat directive, they will be automatically added and removed from gridster controlled view.
EDIT
Here is a plunk demonstrating a slightly modified version of this directive:
angular.module('ngGridster', []);
angular.module('ngGridster').directive('gridster', [
function () {
return {
restrict: 'E',
template: '<div class="gridster"><div ng-transclude/></div>',
transclude: true,
replace: true,
controller: function () {
gr = null;
return {
init: function (elem, options) {
ul = $(elem);
gr = ul.gridster(angular.extend({
widget_selector: 'gridster-item'
}, options)).data('gridster');
},
addItem: function (elm) {
gr.add_widget(elm);
},
removeItem: function (elm) {
gr.remove_widget(elm);
}
};
},
link: function (scope, elem, attrs, controller) {
var options = scope.$eval(attrs.options);
controller.init(elem, options);
}
};
}
]);
angular.module('ngGridster').directive('gridsterItem', [
function () {
return {
restrict: 'EA',
require: '^gridster',
link: function (scope, elm, attrs, controller) {
controller.addItem(elm);
elm.bind('$destroy', function () {
controller.removeItem(elm);
});
}
};
}
]);
If you want you can try and roll your own wrapper for gridster. I spent most of the night last night and it was a little glitchy. The way gridster handles serialization isn't straightforward, etc. Anyway I stumbled on this project and it works really well.
https://github.com/ManifestWebDesign/angular-gridster
I couldn't find an online demo so I made a plunk of it:
http://plnkr.co/edit/r5cSY1USjtr2bSs7rvlC?p=preview
This will be fixed in the next release of
https://github.com/angular-ui/angular-ui/pull/347
The new deferred attribute will take care of the problem as soon as I can figure out why the stupid unit tests won't pass.
There's also Angular Gridster.
Came across it and this question while researching Gridster implementation in Angular.

Resources