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; ...
Related
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.
My html:
<div ng-app="APSApp" class="container">
<br />
<br />
<input type="text" placeholder="Search Terms" />
<br />
<div ng-controller="APSCtl" >
<table class="table">
<tr ng-repeat="r in searchTerms" ng-init="searchTerms=getSearchTerms()" >
<td>{{r.DisplayText}} <input type="text" ng-model="r.SearchInput"></td>
</tr>
</table>
</div>
</div>
<script type="text/javascript">
const moduleId = '#Dnn.ModuleContext.ModuleId';
const tabId = '#Dnn.ModuleContext.TabId';
</script>
<script src="/DesktopModules/RazorCart/Core/Content/Scripts/angular.min.js"></script>
<script src="/DesktopModules/MVC/AdvancedProductSearchMVC/Scripts/AdvancedProductSearch.js"></script>
My angular setup:
var aps = angular.module("APSApp", []);
aps.config(function($httpProvider) {
$httpProvider.defaults.transformRequest = function(data) {
return data !== undefined ? $.param(data) : null;
};
});
aps.factory('SearchTerms',
function($http) {
return {
getSearchTerms: function(onSuccess, onFailure) {
const rvtoken = $("input[name='__RequestVerificationToken']").val();
$http({
method: "post",
url: "/DesktopModules/MVC/AdvancedProductSearchMVC/AdvancedProductSearch/GetAPS",
headers: {
"ModuleId": moduleId,
"TabId": tabId,
"RequestVerificationToken": rvtoken
}
}).success(onSuccess).error(onFailure);
}
};
});
aps.controller('APSCtl',
function(SearchTerms, $scope) {
function getSearchTerms() {
$scope.searchTerms = [];
successFunction = function(data) {
$scope.searchTerms = data;
console.log($scope.searchTerms);
};
failureFunction = function(data) {
console.log('Error' + data);
};
SearchTerms.getSearchTerms(successFunction, failureFunction);
}
function doSomethingElse($scope) {}
});
I'm trying to create a single controller with multiple functions. This works if my angular controller looks like this (and I don't use ng-init):
aps.controller('APSCtl',
function(SearchTerms, $scope) {
$scope.searchTerms = [];
successFunction = function(data) {
$scope.searchTerms = data;
console.log($scope.searchTerms);
};
failureFunction = function(data) {
console.log('Error' + data);
};
SearchTerms.getSearchTerms(successFunction, failureFunction);
});
I was just trying to keep related functions in a single controller. What am I doing wrong? Do I actually have to set up a different controller for each function?
You do not have to assign the value in the template, you can just call the function,
<table class="table" ng-init="getSearchTerms()>
<tr ng-repeat="r in searchTerms" >
<td>{{r.DisplayText}} <input type="text" ng-model="r.SearchInput"></td>
</tr>
</table>
you should have a function named getSearchTerms() in your controller to get it called,
aps.controller('APSCtl',
function(SearchTerms, $scope) {
$scope.getSearchTerms() {
$scope.searchTerms = [];
successFunction = function(data) {
$scope.searchTerms = data;
console.log($scope.searchTerms);
};
failureFunction = function(data) {
console.log('Error' + data);
};
SearchTerms.getSearchTerms(successFunction, failureFunction);
}
function doSomethingElse($scope) {}
});
I am using ControllerAs in angular with ui.router, I have an API in PHP when I call the API and set the scope variable by vm approach for templates then it works accordingly and when I want to delete some record set
and update the vm.servers variable again then template not change according to the newly updated object.
function serverController( server, $state, $rootScope, $scope)
{
var vm = this;
vm.delete = function(server_id) {
vm.loader = false;
server.delete('server/' + server_id)
.then(
function(response){
if(response.status === 200 && !response.data.status) {
alert(response.data.message);
} else if(response.status === 200 && response.data.status){
server.setRootScope().then(
function(){
vm.servers = $rootScope.servers;
$state.go($state.current, {}, {reload: true});
}
);
}
}, function(response) {
if(response.status === 401) {
$state.go('login');
}
}
);
};
if($rootScope.servers == undefined) {
server.get('server')
.then(
function (response) {
if (response.status === 200) {
vm.servers = response.data;
$rootScope.servers = {};
angular.forEach(response.data, function (val) {
if('running' === val.status) {
val['serverState'] = true;
} else {
val['serverState'] = false;
}
$rootScope.servers[val.id] = val;
});
}
},
function (response) {
if (response.status === 401) {
$state.go('login');
}
});
} else {
vm.servers = $rootScope.servers;
}
}
Template File.
<table class="table movietable" width="70%" border="1">
<tr ng-repeat="server in serverModel.servers">
<td width="85%">
<table>
<tr>
<td><b>Server Label: </b> {{server.label}}</td>
</tr>
<tr>
<td>Status: {{server.status}}</td>
</tr>
<tr>
<td>Created At: {{server.created_at}}</td>
</tr>
</table>
</td>
</tr>
</table>
I look your code. I found a problem that,
You are not updating the vm.servers with
the response data. As the values in $rootScope.servers might
be older. So with every delete function call you will have to either update the $rootScope.servers or vm.servers with new data.
I have create a small demo from your code, hope it will help you
identify the problem. In this demo I have first load the data in table
after this, on a button click deleting the record by id and updating
the vm.servers.
My Controller
.controller('Controller',['$rootScope', function($rootScope) {
var vm =this;
vm.customer = {
name: 'Naomi',
address: '1600 Amphitheatre'
};
vm.delete = function(server_id) {
vm.loader = false;
// added some value to $rootScope.servers or you can update it with response data. This is where you will need to update your logic.
$rootScope.servers = [
{ id: 1,
name: 'Naomi1',
address: '1600 Amphitheatre1'
},
{
id: 2,
name: 'Naomi2',
address: '1600 Amphitheatre2'
}
];
angular.forEach($rootScope.servers, function(value,key) {
if(value.id == server_id) {
$rootScope.servers.splice(key,1);
}
});
console.log($rootScope.servers);
//Here I have assign new $rootScope.servers.
vm.servers = $rootScope.servers;
};
var val = {};
vm.init = function() {
vm.servers = [{
id: 1,
name: 'Naomi1',
address: '1600 Amphitheatre1'
},{
id: 2,
name: 'Naomi2',
address: '1600 Amphitheatre2'
}];
$rootScope.servers = {};
val['serverState'] = true;
$rootScope.servers[val.id] = val;
}
vm.init();
}])
index.html
<div ng-controller="Controller as vm">
<table class="table movietable" width="70%" border="1">
<tr>
<td width="85%">
<table>
<tr ng-repeat="server in vm.servers">
<td><b>Server Label: </b> {{server.name}}</td>
<td>Status: {{server.address}}</td>
<td><button ng-click="vm.delete(server.id);">Delete</button></td>
</tr>
</table>
</td>
</tr>
</table>
</div>
Hope this will help you !
Cheers,
Jimmy
In my angular js project factory is not providing values to the controller as needed. I always get empty result in view. When i logged in browser using console.log() all i can see in console is :
[object Object],[object Object],[object Object]. I am stuck at this. Tried many things but nothing worked.
This is my controller code:
var controllers = {};
controllers.ProductController = function ($scope, $route, $routeParams, $location, ProductFactory) {
$scope.products = [];
var init = function () {
$scope.products = ProductFactory.getProducts();
console.log('got products in controller');
console.log($scope.products)
};
var initProductEdit = function () {
var code = $routeParams.code;
if (code = undefined) {
$scope.currentProduct = {};
}
else
{
$scope.currentProduct = ProductFactory.loadProductByCode(code);
}
};
$scope.$on('$viewContentLoaded', function () {
var templateUrl = $route.current.templateUrl;
if (templateUrl == '/Partials/ProductEdit.html') {
initProductEdit();
}
else if (templateUrl == '/Partials/ProductList.html')
{
var code = $routeParams.code;
if(code!=undefined)
{
$scope.deleteProduct(code);
}
}
});
init();
$scope.saveProduct = function () {
ProductFactory.saveProduct($scope.currentProduct);
$location.search('code', null);
$location.path('/');
};
$scope.deleteProduct = function (code) {
ProductFactory.deleteProduct(code);
$location.search('code', null);
$location.path('/');
};
};
angSPA.controller(controllers);
This is my factory code:
angSPA.factory('ProductFactory', function () {
var products = [
{ code: 1, name: 'Game of Thrones', description: 'Series' }
{ code: 2, name: 'DmC', description: 'Game' },
{ code: 3, name: 'Matrix', description: 'Movie' },
{ code: 4, name: 'Linkin Park', description: 'Music Band' }];
var factory = {};
console.log('initializing factory');
factory.getProducts = function () {
console.log('factory now providing products');
return products;
};
factory.loadProductByCode = function (code) {
var product;
for (var i = 0; i < products.length; i++) {
if (products[i].code == code) {
product = products[i];
return product;
}
}
};
factory.saveProduct = function (product) {
products.push(product);
console.log('factory saved product');
};
factory.deleteProduct = function (code) {
var product = factory.loadProductByCode(code);
if (product != null) {
products.remove(product);
console.log('factory deleted product');
}
};
console.log('returning factory');
return factory;
});
This is my view:
<div class="container">
<h2 class="page-title">Product Listing</h2>
<div class="searchbar">
<ul class="entity-tabular-fields">
<li>
<label>Search: </label>
<span class="field-control">
<input type="text" data-ng-model="filter.productName" />
</span>
<label></label>
</li>
</ul>
</div>
<h2>Add New Product</h2>
<table class="items-listing">
<thead>
<tr>
<th>Code</th>
<th>Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td data-ng-repeat="product in products|filter:filter.productName"></td>
<td>{{product.code}}</td>
<td>{{product.name}}</td>
<td>{{product.description}}</td>
<td>Delete</td>
</tr>
</tbody>
</table>
</div>
My routing function:
angSPA.config(function ($routeProvider) {
$routeProvider
.when(
'/',
{
controller: 'ProductController',
templateUrl: 'Partials/ProductList.html'
})
.when(
'/ProductEdit',
{
controller: 'ProductController',
templateUrl: 'Partials/ProductEdit.html'
})
.otherwise({
redirectTo: '/'
});
console.log('routing done');
});
Change your htmt given
var angSPA = angular.module('angSPA', []);
angSPA.controller("ProductController", function($scope, ProductFactory) {
$scope.products = [];
var init = function() {
$scope.products = ProductFactory.getProducts();
console.log('got products in controller');
console.log($scope.products + "")
};
init();
});
angSPA.factory('ProductFactory', function() {
var products = [
{code: 1, name: 'Game of Thrones', description: 'Series'},
{code: 2, name: 'DmC', description: 'Game'},
{code: 3, name: 'Matrix', description: 'Movie'},
{code: 4, name: 'Linkin Park', description: 'Music Band'}];
var factory = {};
console.log('initializing factory');
factory.getProducts = function() {
console.log('factory now providing products');
return products;
};
factory.loadProductByCode = function(code) {
var product;
for (var i = 0; i < products.length; i++) {
if (products[i].code == code) {
product = products[i];
return product;
}
}
};
factory.saveProduct = function(product) {
products.push(product);
console.log('factory saved product');
};
factory.deleteProduct = function(code) {
var product = factory.loadProductByCode(code);
if (product != null) {
products.remove(product);
console.log('factory deleted product');
}
};
console.log('returning factory');
return factory;
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.min.js"></script>
<body ng-app="angSPA" ng-controller="ProductController">
<div class="container">
<h2 class="page-title">Product Listing</h2>
<div class="searchbar">
<ul class="entity-tabular-fields">
<li>
<label>Search: </label>
<span class="field-control">
<input type="text" data-ng-model="filter.productName" />
</span>
<label></label>
</li>
</ul>
</div>
<h2>Add New Product</h2>
<table class="items-listing">
<thead>
<tr>
<th>Code</th>
<th>Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr data-ng-repeat="prod in products|filter:filter.productName">
<td ></td>
<td>{{prod.code}}</td>
<td>{{prod.name}}</td>
<td>{{prod.description}}</td>
<td>Delete</td>
</tr>
</tbody>
</table>
</div>
Your ng-repeat directive should be on the tr element and not the td.
<tr data-ng-repeat="product in products|filter:filter.productName">
Not the cause of your problem, but to log in a service or controller, you can use the $log service and stringify to serialize your objects.
$log.debug(JSON.stringify($scope.products));
Looking at your code, you do $scope.products = [] right at the beginning. This will make angular watch the empty array.
In your init function you assign the products array to $scope.products. But as angular is still watching the initial array it will not be aware of the change.
The solution is to delete the initial assignment $scope.products = [] and make sure to alter the original array but never set it to a new array.
BTW: you could do console.log(JSON.stringify($scope.products)) to get better log information.
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
},