I am trying to call imagesLoaded function (which is imported properly in the scripts area), but when calling it from inside a component prop, I get an error that it is indefined.
my code:
var MasonryContainer = React.createClass({
imagesLoadedFunc: function() { //omitting the imageloaded call here fix everything
this.imagesloaded();
this.refs[reference].getDOMNode().imagesLoaded(function() {
this.masonry.layout()
});
},
componentDidMount: function() {
if (!isBrowser) return;
this.initializeMasonry();
this.performLayout();
this.imagesLoadedFunc();
},
componentDidUpdate: function() {
if (!isBrowser) return;
this.performLayout();
this.imagesLoadedFunc(this);
},
domChildren: [],
initializeMasonry: function(force) {
if (!this.masonry || force) {
this.masonry = new Masonry(this.refs[reference].getDOMNode(), options);
this.domChildren = this.getNewDomChildren();
}
},
getNewDomChildren: function () {
var node = this.refs[reference].getDOMNode();
var children = options.itemSelector ? node.querySelectorAll(options.itemSelector) : node.children;
return Array.prototype.slice.call(children);
},
diffDomChildren: function() {
var oldChildren = this.domChildren;
var newChildren = this.getNewDomChildren();
var removed = oldChildren.filter(function(oldChild) {
return !~newChildren.indexOf(oldChild);
});
var added = newChildren.filter(function(newChild) {
return !~oldChildren.indexOf(newChild);
});
var moved = [];
if (removed.length === 0) {
moved = oldChildren.filter(function(child, index) {
return index !== newChildren.indexOf(child);
});
}
this.domChildren = newChildren;
return {
old: oldChildren,
'new': newChildren, // fix for ie8
removed: removed,
added: added,
moved: moved
};
},
performLayout: function() {
var diff = this.diffDomChildren();
if (diff.removed.length > 0) {
this.masonry.remove(diff.removed);
this.masonry.reloadItems();
}
if (diff.added.length > 0) {
this.masonry.appended(diff.added);
}
if (diff.moved.length > 0) {
this.masonry.reloadItems();
}
this.masonry.layout();
},
componentWillReceiveProps: function() {
setTimeout(function() {
this.masonry.reloadItems();
this.forceUpdate();
}.bind(this), 0);
},
render: function () {
return (
<div className="content" ref="masonryContainer">
<div className='Item'>
<img src="/img/gallery/3.jpg"></img>
</div>
<div className='Item'>
<img src="/img/gallery/11.jpg"></img>
</div>
<div className='Item'>
<img src="/img/gallery/12.jpg"></img>
</div>
<div className='Item'>
<img src="/img/gallery/12.jpg"></img>
</div>
<img src="/img/gallery/4.jpg"></img>
<img src="/img/gallery/5.jpg"></img>
<img src="/img/gallery/6.jpg"></img>
<img src="/img/gallery/7.jpg"></img>
<img src="/img/gallery/8.jpg"></img>
<img src="/img/gallery/9.jpg"></img>
</div>
);
}
});
React.render(
<MasonryContainer/>, document.getElementById('reactbody')
)
</script>
if I call the imageloaded constructor outside of the react component, it is working.
What am I missing?
Your calling imagesloaded using this,
this.imagesloaded();
However, imagesloaded is not part of your component, nor a standard in React. Thus, this.imagesloaded is undefined, since it never has been declared. Try removing the "this" part of the statement.
imagesLoadedFunc: function() {
imagesloaded();
//the rest of the method
},
Related
So, I'm trying to create a hierarchical tree. Whena node is selected that has children, then all the children of the node is selected, but when I select all the children I also want to select the parent.
here is a link to the plunker:
[https://plnkr.co/plunk/iMBFfy6cf7urOHhZ][1]
I have created a directory to markup the tree
TreeController.js
(function (ng) {
var app = ng.module('tree', ['tree.service', 'tree.directives']);
app.controller("TreeController", ["TreeService", "$scope", function (TreeService, $scope) {
var tc = this;
buildTree();
function buildTree() {
TreeService.getTree().then(function (result) {
tc.tree = result.data;
}, function (result) {
alert("Tree no available, Error: " + result);
});
}
$scope.selectedItems = [];
$scope.getSelected = function(){
$scope.selectedItems = [];
function checkChildren(c) {
angular.forEach(c.children, function (c) {
if (c.checked){
$scope.selectedItems.push({"selected":c.name});
}
checkChildren(c);
});
}
angular.forEach(tc.tree, function(value, key) {
if (value.checked){
$scope.selectedItems.push({"selected":value.name});
}
checkChildren(value);
});
};
}]);
})(angular);
index.html
<div ng-controller="TreeController as tc">
<ul class="tree">
<node-tree children="tc.tree"></node-tree>
</ul>
<button ng-click="getSelected()">Get Selected</button>
<br/>
<br/>
Selected:
<ul>
<li ng-repeat="item in selectedItems">
{{item.selected}}
</li>
</ul>
</div>
TreeDirective.js
(function (ng) {
var app = ng.module('tree.directives', []);
app.directive('nodeTree', function () {
return {
template: '<node ng-repeat="node in tree"></node>',
replace: true,
restrict: 'E',
scope: {
tree: '=children'
}
};
});
app.directive('node', function ($compile) {
return {
restrict: 'E',
replace: true,
templateUrl: 'node.html', // HTML for a single node.
link: function (scope, element) {
/*
* Here we are checking that if current node has children then compiling/rendering children.
* */
if (scope.node && scope.node.children && scope.node.children.length > 0) {
scope.node.childrenVisibility = true;
var childNode = $compile('<ul class="tree" ng-if="!node.childrenVisibility"><node-tree children="node.children"></node-tree></ul>')(scope);
element.append(childNode);
} else {
scope.node.childrenVisibility = false;
}
},
controller: ["$scope", function ($scope) {
// This function is for just toggle the visibility of children
$scope.toggleVisibility = function (node) {
if (node.children) {
node.childrenVisibility = !node.childrenVisibility;
}
};
// Here We are marking check/un-check all the nodes.
$scope.checkNode = function (node) {
node.checked = !node.checked;
// if (node.checked){
// alert("clicked");
// }
function checkChildren(c) {
angular.forEach(c.children, function (c) {
c.checked = node.checked;
checkChildren(c);
});
}
checkChildren(node);
};
}]
};
});
})(angular);
node.html
<li>
<span ng-click="toggleVisibility(node)"> {{ ( node.childrenVisibility && node.children.length ) ? '+' : '-' }}</span>
<input ng-click="checkNode(node)" type="checkbox" ng-checked="node.checked">
<span>
{{ $index + 1 }}. {{ node.name }}
</span>
</li>
The first step is to determine what each node's parent node is. We can do that by recursing right after the tree is loaded and setting a parent property on each node.
TreeController.js
...
function buildTree() {
TreeService.getTree().then(function (result) {
tc.tree = result.data;
function setParentForChildren(n) {
angular.forEach(n.children, function (c) {
c.parent = n;
setParentForChildren(c);
})
}
angular.forEach(tc.tree, setParentForChildren);
}, function (result) {
alert("Tree no available, Error: " + result);
});
}
...
Now, we can use that parent reference each time a box is checked to recurse up the tree and say "if all my children are checked, then I should be checked too" for each parent node.
TreeDirective.js
...
$scope.checkNode = function (node) {
node.checked = !node.checked;
function checkParent(n) {
if (!n.parent)
return;
const p = n.parent;
p.checked = p.children.every(function(c) { return c.checked });
checkParent(p);
}
checkParent(node);
function checkChildren(c) {
angular.forEach(c.children, function (c) {
c.checked = node.checked;
checkChildren(c);
});
}
checkChildren(node);
};
...
Link to modified plunker
I'm new to angularJS, and now I'm trying to realize some parts.
The questions is: how do I get access to callback onFinish() which is passed to component "my-timer" and run it? this.onFinish() returns the error.
Here is my markup:
<div ng-app="app" ng-controller="MyCtrl as myCtrl">
<div>
Status: {{myCtrl.status ? myCtrl.status : 'Waiting...'}}
</div>
<div>
<button ng-click="myCtrl.addTimer(5)">Add timer</button>
</div>
<div ng-repeat="timer in myCtrl.timers">
<div>
<h3>Timer {{timer.id}}</h3>
<button ng-click="myCtrl.removeTimer($index)">X</button>
<my-timer id="{{timer.id}}" start-seconds="{{timer.seconds}}" on-finish="myCtrl.onFinish(endTime)"></my-timer>
</div>
</div>
</div>
And here is index.js
var app = angular.module('app', []);
app.controller('MyCtrl', class {
constructor($scope) {
this.status = null;
this.timerId = 0;
this.timers = [];
this.addTimer(10);
this.addTimer(3);
console.log($scope);
}
addTimer(seconds) {
this.timers.push({
id: this.timerId++,
seconds
});
}
removeTimer(index) {
this.timers.splice(index, 1);
}
onFinish(endTime){
this.status = `Timer finished at ${endTime}`;
console.log(endTime);
}
});
app.component('myTimer', {
bindings: {
id: '#',
startSeconds: '#',
onFinish: '&',
},
controller: function($interval, $scope) {
this.endTime = null;
this.$onInit = function() {
this.countDown();
};
this.countDown = function() {
$interval(() => {
this.startSeconds = ((this.startSeconds - 0.1) > 0) ? (this.startSeconds - 0.1).toFixed(2) : 0;
}, 100);
};
},
template: `<span>{{$ctrl.startSeconds}}</span>`,
});
And here is jsFiddle
this.$onInit = function() {
this.countDown();
};
this.onFinish('1');
The problem here is that you tried to execute this.onFinish right in controller's body. And that wont work that way. If you want this function to be called during initialization, move it to $onInit
this.$onInit = function() {
this.countDown();
this.onFinish('1');
};
Otherwise, call it from another component method. You can only declare variables and component methods in controller body, but not call functions.
I have a html like this :
<div id="create-group" ng-controller="groupCreateController">
<div id="container">
<h1>Create group</h1>
<div class="row">
<div class="col-md-4"><input placeholder="Group Name.." ng-model="group.name"></div>
<div class="col-md-8">
<label>Group Description : </label>
<textarea ng-model="group.description"> </textarea>
</div>
</div>
<br/>
<div class="row">
<div class="col-sm-6">
<usermgr-permission-list group="group"></usermgr-permission-list>
<button type="button" class="btn btn-md btn-primary" ng-click="btnSave_click($event)">SAVE</button>
</div>
<div class="col-sm-6">
<usermgr-user-list group="group"></usermgr-user-list>
</div>
</div>
</div>
</div>
My controller is :
(function (module) {
'use strict';
module.controller('groupCreateController', function ($scope, $rootScope, $routeParams, $location, userGroupService, $mdDialog) {
$scope.group = [];
$scope.init = function () {
if ($routeParams.hasOwnProperty('id')) {
//edit mode
// $scope.trans.heading = 'Edit Release';
// $scope.trans.saveBtn = 'Save';
var id = parseInt($routeParams.id);
getUserGroup(id);
} else {
$scope.group[0].id = 0;
$scope.group[0].permissions = [];
$scope.assignedPermissions = [];
$scope.enrolledUsers = [];
$scope.group[0].users = [];
$scope.group[0].name = '';
$scope.group[0].description = '';
}
};
function getUserGroup(id) {
userGroupService.getbyid(id).then(function (info) {
if (info !== undefined && info.id === id) {
$scope.group[0].id = info.id;
$scope.group[0].name = info.name;
$scope.group[0].description = info.description;
console.log($scope.group);
// $rootScope.$broadcast('rCube-user-mgt-users-list', info.id);
// $rootScope.$broadcast('rCube-user-mgt-permissions-list', info.id);
}
else {
}
}).catch(function (exception) {
console.error(exception);
});
}
$scope.init();
});
})(angular.module('r-cube-user-mgt.user-group'));
I have two custom directives in the first block of code for user permissions and users. The group scope that i pass with the directive does not contain the values i put in the getUserGroup(id) function. The group name and group description shows up so the scope.group in the controller is filled, however thats not the case once i pass it to my directives. here is the directives code as well :
permissions list :
(function (module) {
'use strict';
module.directive('usermgrPermissionList', function () {
return {
restrict: 'E',
scope:{
group: '='
},
controller: function ($scope, permissionService) {
$scope.updatedPermissions=[];
console.log($scope.group); //it doesnt have the values from the controller ..
if (!$scope.group.hasOwnProperty('permissions')) {
$scope.group.permissions = [];
}
function getData() {
console.log("inside getDAta for permission list" + $scope.group.id;
permissionService.getPermissionsFiltered($scope.group.id).then(function (info) {
if (info && info.length > 0) {
console.log(info);
$scope.group.permissions = info.map(function (a, index, array) {
return {
id: a.id,
name: a.name,
description: a.description,
assigned: a.assigned
};
});
}
}).catch(function (exception) {
console.error(exception);
});
} //end of getData()
$scope.init = function () {
getData();
};
$scope.init();
},
templateUrl: 'r-cube-user-mgt/permission/list/list.tpl.html'
};
});
})(angular.module('r-cube-user-mgt.permission'));
can anyone help?
you cannot assign property to an array like this $scope.group.id = 0;
either make $scope.group object
$scope.group = {};
or add properties to an index
$scope.group = [];
$scope.init = function () {
if ($routeParams.hasOwnProperty('id')) {
//edit mode
// $scope.trans.heading = 'Edit Release';
// $scope.trans.saveBtn = 'Save';
var id = parseInt($routeParams.id);
getUserGroup(id);
} else {
$scope.group[0].id = 0;
$scope.group[0].permissions = [];
$scope.assignedPermissions = [];
$scope.enrolledUsers = [];
$scope.group[0].users = [];
$scope.group[0].name = '';
$scope.group[0].description = '';
}
};
So I solved the issue by adding broadcast to send the id when the directive loads. This worked!
in the Group controller i add broadcast and send the group.id
function getUserGroup(id) {
userGroupService.getbyid(id).then(function (info) {
if (info !== undefined && info.id === id) {
$scope.group.id = info.id;
$scope.group.name = info.name;
$scope.group.description = info.description;
console.log($scope.group);
$rootScope.$broadcast(rCubeTopics.userMgtPermissionLoadData, $scope.group.id);
}
}).catch(function (exception) {
console.error(exception);
});
}
and in the permission directive get that broadcast :
$scope.$on(rCubeTopics.userMgtPermissionLoadData, function (event, id) {
console.log($scope.group.id);
getData();
});
I have a React.js app that pulls the top 30 stories from the HackerNews API, and displays the first 30 on the page. As of right now, my render function for StoryTop displays the correct link to retrieve an individual story from their API, which is also displayed on screen. Where I am having trouble is passing this link to the Story class, where I am getting the error "src is not defined".
var StoryTop = React.createClass({
getInitialState: function() {
return {
content: []
};
},
componentDidMount: function() {
var src ="https://hacker-news.firebaseio.com/v0/topstories.json?print=pretty"
$.get(src, function(result) {
var stories = result;
if (this.isMounted()) {
this.setState({
content: stories.slice(0,30)
});
}
}.bind(this));
},
render: function() {
var storyNodes = this.state.content.map(function(item) {
var src ="https://hacker-news.firebaseio.com/v0/item/" + item + "/.json?print=pretty";
return (
<tr key={item}>
<td>
{src}
<Story/>
</td>
</tr>
);
});
return (
<table>
<tbody>
{storyNodes}
</tbody>
</table>
);
}
});
React.render(
<StoryTop />,
newstories
);
var Story = React.createClass({
getInitialState: function() {
return {
by: '',
title: '',
score: '',
url: ''
};
},
componentDidMount: function() {
$.get(src, function(result) {
var story = result;
if (this.isMounted()) {
this.setState({
by: story.by,
score: story.score,
url: story.url,
title: story.title,
});
}
}.bind(this));
},
render: function() {
var divclass = 'indivstory';
var topPClass = 'storytop';
var bottomPClass= 'storybottom';
return (
<div className={divclass}>
<p className={topPClass}> <span></span><img src="./images/uparrow.gif"></img> <span><a href={this.state.url}>{this.state.title} ({this.state.url.replace(/^https?:\/./,'').replace(/\/.*$/,'')}</a>).</span> </p>
<p className={bottomPClass}> {this.state.score} points by {this.state.by} | discuss </p>
</div>
);
}
});
EDIT FIXED:
Passed the var src to the Story :
var src ="https://hacker-news.firebaseio.com/v0/item/" + item + "/.json?print=pretty";
return (
<tr key={item}>
<td>
{src}
---> <Story link = {src} />
</td>
</tr>
Used "link" to get story data:
componentDidMount: function() {
$.get(this.props.link, function(result) {
var story = result;
It looks like componentDidMount is calling for src but src isn't defined. within the local block scope.
componentDidMount: function() {
$.get(this.render.src, function(result) {
var story = result; ...
I have this factory:
.factory('Options', function () {
var getOptions = function () {
var storageData = sessionStorage.siteOptions;
if (storageData !== 'undefined')
return angular.fromJson(storageData);
return {
rotateBackground: false,
enableMetro: true
};
};
var saveOptions = function (options) {
sessionStorage.siteOptions = angular.toJson(options);
}
return {
get: getOptions,
save: saveOptions
};
});
which works fine on my profile page:
.controller('ProfileController', ['Options', function (options) {
var self = this;
self.options = options.get();
self.save = function () {
options.save(self.options);
}
}]);
The html looks like this:
<div class="row" ng-controller="ProfileController as profile">
<div class="large-4 columns">
<h2>Site options</h2>
<form name="optionsForm" ng-submit="profile.save()" role="form">
<div class="row">
<div class="large-12 columns">
<input id="enable-metro" type="checkbox" ng-model="profile.options.enableMetro"><label for="enable-metro">Enable metro design</label>
</div>
<div class="large-12 columns">
<input id="enable-background-rotate" type="checkbox" ng-model="profile.options.rotateBackground"><label for="enable-background-rotate">Enable rotating background</label>
</div>
<div class="large-12 columns">
<button class="button">Save</button>
</div>
</div>
</form>
</div>
</div>
But I have this other page that has a controller that needs to be aware if the options are ever saved. Basically, if saveOptions is ever called, then I need any page that looks at options to be notified.
The reason for this, is for example:
.controller('MetroController', ['Options', function (options) {
scope.options = options.get();
scope.$watch(function () {
return options.get();
}, function () {
scope.options = options.get();
});
}])
// ---
// DIRECTIVES.
// ---
.directive('metro', function () {
return {
restrict: 'A',
controller: 'MetroController',
controllerAs: 'metro',
link: function (scope, element, attr) {
scope.$watch(function () {
return metro.options.enableMetro;
}, function (enableMetro) {
if (enableMetro) {
element.addClass('metro');
} else {
element.removeClass('metro');
}
});
}
}
});
As you can see, this is trying to apply a class based on the enableMetro flag. But when I run this, I get an error about the amount of iterations this has had to loop through.
Can someone help me with this?
I think I have this solved.
I changed my options factory to this:
.factory('Options', function () {
var getOptions = function () {
var storageData = sessionStorage.siteOptions;
if (storageData !== 'undefined')
return angular.fromJson(storageData);
return {
rotateBackground: false,
enableMetro: true
};
};
var saveOptions = function (options) {
sessionStorage.siteOptions = angular.toJson(options);
current = getOptions();
}
var current = getOptions();
return {
current: current,
save: saveOptions
};
});
then in my controllers, I just did this:
.controller('MetroController', ['$scope', 'Options', function ($scope, options) {
var self = this;
self.options = options.current;
$scope.$watch(function () {
return options.current;
}, function () {
self.options = options.current;
});
}])
// ---
// DIRECTIVES.
// ---
.directive('metro', function () {
return {
restrict: 'A',
controller: 'MetroController',
link: function (scope, element, attr, controller) {
scope.$watch(function () {
return controller.options.enableMetro;
}, function (enableMetro) {
if (enableMetro) {
element.addClass('metro');
} else {
element.removeClass('metro');
}
});
}
}
});
and that seems to work fine.