AngularJS bind data to a compiled element - javascript

I'm creating elements inside a for loop using $compile and I need to bind data to them.
for (var item in data) {
var elem = $compile('<panel ng-model="item"></panel>')($scope)[0];
container.append(elem);
}
How can I get the panel directive to access the item data? I have to append the elements manually, so I can't use ng-repeat.

Don't do a loop and compile for each item. That would be bad performance for your app. Do:
$scope.data = data;
var elem = $compile('<div ng-repeat="item in data"><panel ng-model="item"></panel></div>')($scope)[0];
container.append(elem);
You place data on the $scope, because that's how the binding occurs. Note that the HTML you write is compiled against a specific $scope.
I think you can do without the [0] as well, but not sure.

I've solved my problem like this:
for (var index in data) {
var elem = $compile('<panel data="data[' + index + ']"></panel>')($scope)[0];
container.append(elem); // pseudocode line
}

Related

Json data from table in an onclick function show in seperate div

I'm trying to get data used in my table to be used in a div when I click on the table. The thing is, there are multiple tables in my script according to the data of my JSON. So my JSON consists of object that consists of object. For example:
My table(s) are rendered like this:
data.forEach(function(somedata){
return '<table><tr><td>'+somedata.something+'</td></tr></table>';
});
Now I've tried to get the onclick to work in this case but I cant seem to figure out how. I'd like to not use specific ID's rendered in the foreach like:
var i=0;
data.forEach(function(somedata){
i++;
return '<table id="'.id.'"><tr><td>'+somedata.something+'</td></tr></table>';
});
the variable somedata consists of an object so I cant just make an onclick in the html code of the table either and send the data in it.
So somedata would look something like this but json encoded:
somedata{
[0]=>array(
'something'=>'test',
'theobject'=>array(...)
),
[1]=>array(etc...)
}
Now what I want is to get the data from theobject in a seperate div as soon as I click on the table that belongs to the right somedata.
I've been thinking of making a jquery on click for this but then I would need specific ID's in the table(if that's the only possible solution then I'd take it). Cant I do something with this or something? Or send the data at the same time it's being rendered cause in my code I can at the moment of course reach to somedata.theobject
I think I'm thinking a bit too difficult about this. Am I?
You can pass this in onclick function like
return '<table onclick=makeObject(this)><tr><td>'+somedata.something+'</td></tr></table>';
And then use the function to get the data
function makeObject(that){
var tbl = $(that).find('tr').map(function() {
return $(this).find('td').map(function() {
return $(this).html();
}).get();
}).get();
}
There are a few ways to go about this. Rather than using the forEach function we can use the jQuery.map function, since you've indicated that you're open to using jQuery :-)
var $els = $.map(data, function(somedata, i){
var $el = $('<table><tr><td>'+somedata.something+'</td></tr></table>')
.click(function(e) {
populateDivWithData(somedata.theobject);
});
return $el;
});
The call to .click inside each will create a separate click handler for each item in data; each click handler then has access to the relevant theobject value.
EDIT: Thanks #Loko for the reminder about the .forEach built-in

Angular directive and SVG

so, this is a little complicated.
I have a directive that loads an SVG file based on some criteria. It is a large directive so I won't be posting it here as it does not affect my issue, you just need to know it exists.
In that directive I have a template file that has this bit of HTML:
<div ng-include="svgPath" onload="loaded()"></div>
note the onload function. This is called when the SVG has loaded.
Inside this SVG there are some hidden groups that I need to interact with. Because this is changing the DOM I have written another directive, which looks like this:
.directive('kdGraphicsRepeater', function () {
return {
restrict: 'A',
scope: {
targetElementId: '#kdGraphicsRepeater'
},
templateUrl: '/assets/tpl/directives/graphicsRepeater.tpl.html',
link: function (scope, element) {
// Declare our options
scope.options = [];
// Get our target elements
var target = angular.element(document.getElementById(scope.targetElementId)),
svg = angular.element(target.find('svg')),
children = svg.children();
console.log(target.length);
console.log(svg.length);
console.log(children.length);
// Loop through the SVG children
for(var i = 0; i < children; i++) {
// Get the current child
var child = children[i],
childId = child.attr('id');
console.log(childId);
// If we have an option
if (childId && childId.indexOf('options-') > -1) {
// Push the name to our options array
scope.options.push(childId.replace('options-'));
}
}
}
}
})
and the corresponding template looks like this:
<div ng-repeat="item in options">
{{ item }}
</div>
As you should be able to make out. I am trying to loop through the SVG immediate children and check to see if any of the elements have a partial id match to options-. If they do I add them (omitting the "options-" string) to the scope.options variable. My template simply loops through them displays them.
The problem is that the SVG has not loaded for this directive and the console.log(svg.length) returns 0 for that very reason. I need some way of knowing when the SVG has loaded.
Can someone help me out?
Okay, I'm not sure about this but maybe try using the ng-if directive by angular.
in your loaded() function, have like a boolean that says its loaded, say its called svgLoaded. use ng-init instead of onload. Make sure this would be the same scope as the one in your svg.
then in your svgPath, bind ng-if to your boolean
<"element where you are attaching kdGraphicsRepeater"
ng-if="svgLoaded" kdGraphicsRepeater
>
Sorry, I'm making assumption that you have same scope, and that is possible to do.

AngularJS regex json data for img src or other selectors

I'm new to angular, so bear with me here.... I've a simple, but dirty, json results file I am pulling into my angularjs page. I am trying to get just the img src from the first img that is found in the returned info. Here is a fiddle with it setup:
var app = angular.module('myApp', ['ngSanitize']);
app.controller('MainCtrl', function ($scope) {
$scope.data = "HEREHLKSDJFKLFJSLK:FJKFJ:KLJSL:KDfj<img src=\"http://www.sierraavalanchecenter.org/sites/default/files/forecast/201411/20141113-074138-0.png\"/ >asdfsdjflksdfj;kasdfkjs;dklfs;df";
});
app.filter('extractSrcUrl', function () {
return function (text) {
var output = angular.element(text).attr('src');
return(output);
}
});
http://jsfiddle.net/j9jst560/
If found the filter idea via this post: getting img src from json data while using AngularJS ng-bind-html
If I get rid of all the json data and just leave: http://www.sierraavalanchecenter.org/sites/default/files/forecast/201411/20141113-074138-0.png\"/ > the filter pulls out the src fine.
However, with my messy data, no results. Should I be using regex somewhere in my controller rather than running filter? Is my data that bad?
You could do either of the following.
Modify your selector code to the following:
var output = angular.element('<div>' + text + '</div>').find('img')[0].attr('src')
Which is probably the safest and easiest way. Note that in angular 1.2 and below (I believe they fixed it in 1.3) this has to be as follows:
var output = angular.element(angular.element('<div>' + text + '</div>').find('img')[0]).attr('src')
The other way is to extract purely using regex as follows:
var output = text.match(/src="([^"]*)"/i)[1]
As a side note - return isn't a function, normally it's just called as return output;.

Angular: Selecting elements from DOM

I'm using an ng-repeat to create items. I would like to determine the height of each element that is created by using a function.
I know how to pass the index of the element that is created by ng-repeat to the function that should determine the height, but I'm getting stuck in actually selecting that item.
This is what I'm using now:
$scope.getItemHeight = function(index) { // index is index of element in ng-repeat
var itemHeight = angular.element('li').eq('+index+').offsetHeight;
return itemHeight;
};
But that doesn't work due to error: Error: [jqLite:nosel] Looking up elements via selectors is not supported by jqLite!
I also tried:
$scope.getItemHeight = function(index) {
var itemHeight = document.querySelectorAll('ul:nth-child('+index+')');
return itemHeight;
};
This returns an element with length 0 so the selector doesn't work.
What am I missing here?
Codepen here: http://codepen.io/squrler/pen/LxsfE?editors=101
EDIT:
What I want is not possible at this time. More information here: https://github.com/driftyco/ionic/issues/1691
Edit: After looking at this further it seems a bit more complicated. The directive that is firing the repeater needs to run in order for the lis to be rendered, as the li is rendered (assuming you move this to a directive) it triggers the directive to get its height, but the li and it's corresponding data are not yet fully rendered and thus have no height. If you wait for the rendering using timeout, the repeater will just continue rendering without valid height data while the timeout waits. So it seems you have a bit of a conundrum. You might try using http://binarymuse.github.io/ngInfiniteScroll/ or something of the like.
This should be put in a directive which will give you easy access to the li as it is rendered.
Something like:
.directive('getHeight', ['$filter', function ($filter) {
'use strict';
return {
restrict: 'A',
scope: true,
link: function (scope, element, attrs) {
var li = element[0];
var height = li.offsetHeight;
console.log('Height': height)
}
};
}]);
Not sure what you're looking to do with the height once you have it...
Otherwise you can just go:
var ul=document.getElementsByTagName('ul')[0];
var li=ul.getElementsByTagName('li')[index];
var height=li.offsetHeight;

jquery ui - call methods with same name on different widgets

Suppose I have created several widgets (mywidget1, mywidget2, ...) and that all have a method with the same name (doSomething).
To invoke the method I can use:
$("#elem").widget1("doSomething");
but this way I need to know the name of the widget (in the example widget1).
If I have an array with multiple instances of the various widgets, how can I invoke on each one the method "doSomething" without knowing the name of the widget?
You can't. Two options for you:
Store them in separate arrays (one array for widget1, another for widget2), or
Store objects in the arrays containing the information about which widget relates to that entry
Here's an example of #2
var list = [
{widget: "widget1", element: $("#elem")},
{widget: "widget2", element: $(".some-selector")},
{widget: "widget2", element: $("#another")},
{widget: "widget1", element: $("div .target")}
];
$.each(list, function(i, entry) {
entry.element[entry.widget]("doSomething");
});
In theory I suppose you could do something like the following:
var widgets = [widget1, widget2, widget3]; // etc, will assume they are defined already
// Access each Widget, now widget points to each of the widgets
// So we dont need to know the actual name like widget1, widget2 etc
widgets.forEach(function (widget) {
$('#elem').widget('doSomething'); // Call the doSomething method of this widget
$('#elem').widget.call(null, 'doSomething'); // Try this if the above fails
$('#elem').call(widget, 'doSomething'); // Or maybe this :)
}
Anyway try the above, of the top of my head im not sure which will work. I think what you are trying to do might be a bit difficult to implement, so sorry if it doesnt work. Hopefully it will :)
you can have an object of functions and get the key value dynamically to use the particular function
widgets = {
widget1 : function(value){return value;},
widget2 : function(value){return value+1},
widget3 : function(value){return value+2}
}; //you can have your list of functions say widgets1,2,3....
for(var k in widgets ){
console.log(widgets[k](1));
} //to get the function name you can use the key name in the object
Example on https://gist.github.com/vishnu667/44063d64f2d1210c26c9
you can also call the function if you know the key
widgets['widget2'](10); //to directly call the function if you know the key
to Dynamically add more widgets use
function add_widget(name,widgetFunction){ //function to add widget
widgets[name]=widgetFunction;
}
add_widget("widget4",function(value){return value+10;}); //adds a new widget to the widgets list
I found this solution:
before:
var tmp = $("#elem").widget1();
myarray[i] = tmp;
now:
var tmp = $("#elem").widget1();
myarray[i] = tmp.data(ui-widget1);
in this way it is possible to directly call the method:
myarray[x].doSomething();
what do you think?
can be an efficient solution?
thanks to all

Categories