Nested directives won't work as desired - angularjs

I am new to AngularJS, was trying to use nested directives. I have a home layout which has following
<product-list></product-list>
This directive has a template which is present in another file. It has content like that
<div class="products">
<product prod-id="{{product.id}}" ng-repeat="product in products"></product>
</div>
The problem I am facing is when the product-list directive is compiled it isn't able to understand product.id in the expression. It gives me some error like that
Error: Syntax Error: Token 'product.id' is unexpected, expecting [:] at column 3 of the expression [{{product.id}}] starting at [product.id}}].
The directives are defined as
app.directive('productList', function($compile) {
return {
restrict: 'AE',
replace: true,
controller: 'ProductListCtrl',
templateUrl: base_url + 'partials/directives/product-list.html'
};
});
app.directive('product', function() {
return {
restrict: 'AE',
controller: 'ProductCtrl',
templateUrl: base_url + 'partials/directives/product.html',
scope: {
prodId: '=prodId'
},
link: function ($scope, element, attrs) {
var num = $scope.$eval(attrs.prodId);
if(!isNaN(parseInt(num))){
$scope.prodId = num;
}
}
};
});
UPDATE: Added controller for directive
myApp.controller("ProductListCtrl", ['$scope', 'ProductModel', '$stateParams', '$location', function($scope, ProductModel, $stateParams, $location) {
$scope.products = {};
//Fetch the products if we have some category for a given state
if(typeof $stateParams.categoryId != 'undefined' && typeof $stateParams.prodId == 'undefined'){
//Fetch products for selected category and page
ProductModel.getProductsByCategory($stateParams.categoryId, function(products){
$scope.products = products;
});
}
}]);
Please guide what I am doing wrong or missing anything.

Strange thing happened not sure if its the right way, but it worked for me :)
I used
<product prod-id="product.id" ng-repeat="product in products"></product>
instead of
<product prod-id="{{product.id}}" ng-repeat="product in products"></product>
and it worked

Related

Angualrjs- redirect from one controller to another

I have two controllers. I would like to use the other controller based on a dependancy value. I am not able to set this at the $route level. How can I essentially do this on in the main controller: Is this even possible?
angular.module('app').controller('Controller1', ['importantService', function(importantService) {
if (importantService.getValue) {
// Use this other controller Controller2 instead
}
});
You could put your 2 controllers in directives and have an ng-if in the template switch between the two, something like:
app.controller('MainCtrl', function($scope, importantService) {
$scope.importantValue = importantService.getValue;
});
app.directive('myFirstComponent', function() {
return {
restrict: 'E',
templateUrl: '/same/url/for/both',
controller: function() {/*...your controller 1*/}
};
});
app.directive('mySecondComponent', function() {
return {
restrict: 'E',
templateUrl: '/same/url/for/both',
controller: function() {/*...your controller 2*/}
};
});
and in your template
<div ng-if="importantValue === 1"><my-first-component></my-first-component></div>
<div ng-if="importantValue === 0"><my-second-component></my-second-component></div>
probably not the most orthodox solution but it will do what you're asking.

directive unable to retrieve data from a service

I have a little SPA using angular. The concept is simple, after login, $routeProvider redirects to a home page where I have a homeController specified.
this is from my home view that is rendered by ng-view while navigating to "/home" :
<my-directive datas=getData()></my-directive>
<ul>
<li ng-repeat="data in datas"> {{data.title}} {{data.content}} </li>
</ul>
my directive is written as:
angular.module('app').directive('myDirective', ['myService', function (myService) {
return {
restrict: "E",
scope: {
data: '='
},
templateUrl: "partials/my-directive.html",
controller: function ($scope) {
$scope.getDatas = function()
{
myService.retData();
}
}
};
}]);
the home controller is:
angular.module('app').controller('homeController', homeController);
homeController.$inject = ['myService', '$scope'];
function homeController(myService, $scope) {
var vm = this;
vm.data = [];
initController();
function initController() {
vm.data = myService.retData();
}
}
and finally my service is
angular.module('app').service('myService', myService);
function myService () {
var data = [
{ id: 1, title: 'Albert Einstein', content: 'Random Content' }
];
return {
retData: function () {
return data;
},
addData: function (title, content) {
var currentIndex = data.length + 1;
data.push({
id: currentIndex,
title: title,
content: content
});
}
};
}
now that i mentioned everything, here comes the problem. the directive is not able to retrieve data from the service. Actually when i run the project in VS2013, myDirective.js is not even loaded. I included all services, directives, controllers etc in the main HTML page.
What is causing this problem?
Does it have something to do with the scope being isolated in the directive?
What is a better approach to sharing data between a controller, directive and service?
I may have made some silly mistakes while rewriting all the code. Please do point them out, however keep in mind my actual issue and what error may be causing that.
Better to use isolated scope to pass data controller to directive.
Html:
<my-directive datas="getData()" data="data"></my-directive>
Directive:
angular.module('app').directive('myDirective', [function () {
return {
restrict: "E",
scope: {
data: '='
},
templateUrl: "partials/my-directive.html",
link: function (scope) {
//Here you got the isolated scope data
var details = scope.data;
}
};
}]);
OR
app.directive('myDirective', function() {
return {
restrict: 'E',
templateUrl: 'partials/my-directive.html',
scope: {
date: '=',
},
controller : ['$scope', 'myService', function($scope, myService) {
myService.retData();
}],
link: function(scope, elem, attr) {
//
}
};
});

Directives in AngularJS

Hi I am trying to learn AngularJS Directives and I came really close but would like to extend my learning by cleaning and de-coupling my directive code.
Directive:
app.directive('ngSparkline', function () {
var url = "http://api.openweathermap.org/data/2.5/forecast/daily?mode=json&units=imperial&cnt=14&callback=JSON_CALLBACK&q=";
return {
restrict: 'A',
require: '^ngCity',
transclude: true,
scope: {
ngCity: '#'
},
templateUrl: 'app/partials/weatherTemplate.html',
controller: ['$scope', '$http', function($scope, $http) {
$scope.getTemp = function(city) {}
}],
link: function (scope, iElement, iAttrs) {
scope.getTemp(iAttrs.ngCity);
scope.$watch('weather', function (newVal) {
if (newVal) {
var highs = [];
angular.forEach(scope.weather, function (value) {
highs.push(value.temp.max);
});
chartGraph(iElement, highs, iAttrs);
}
});
}
};
});
As you can see I am not trying to write inline template rather use templateUrl. Now the problem is for the controller when I try using a .js controller instead of writing the controller code inline, I receive an error. How do I achieve this.
I tried:
I tried passing
controller: '#',
name: 'ctrl'
and I pass the 'ctrl' as:
<div ng-sparkline ng-city="San Francisco" ctrl="weatherController"></div>
it gives me controller not found. My project structure is something like below.
What am I doing wrong?
Is there a better/correct way of doing this?
Please suggest.
Note: I am learning this exercise from "http://www.ng-newsletter.com/posts/directives.html"
Why not just specify ng-controller on your element? If WeatherController is defined somewhere else then it doesn’t affect your directive definition, just leave the controller out of there.
<div ng-sparkline ng-city="San Francisco" ng-controller="WeatherController"></div>
Provided somewhere you do have the controller defined like
app.controller('WeatherController', ['$scope', '$http', function ($scope, $http) {
$scope.getTemp = // …
}]);
(BTW, if you noticed, the AngularJS convention is to name controllers in UpperCase fashion.)

Directive scope got undefined

I get an issue with passing data to angular directives inside ng-repeat, it always got undefined. Here are my code
The Controller:
angular.module('module').controller('ModuleController', ['$scope', 'MyService', function($scope, MyService) {
$scope.getData = function() {
$scope.data = MyService.myGetRequest(); // returning array of objects
};
});
View:
<div ng-controller="ModuleController" ng-init="getData()" ng-switch="data.length > 0">
<div ng-repeat="d in data" ng-switch-when="true">
<my-directive data="d.object"></my-directive>
</div>
</div>
Directive:
angular.module('module').directive('myDirective', [function() {
return {
restrict: 'E',
template: '<div></div>' // let's ignore the template for now,
scope: { data: '=' },
link: function(scope, el, attrs) {
console.log(scope.data); // always undefined
}
};
}]);
Service:
angular.module('module').factory('MyService', ['$resource', function($resource) {
return $resource('/data/:id',
{ id: '#_id' },
{
myGetRequest: { method: 'GET', isArray: true }
});
}]);
I thought it was because the $scope.data still empty when the template loaded. If yes, anyone know what is the solution? Thanks in advance. :)
EDIT: btw, if I put <my-directive data="data"></my-directive> instead of <my-directive data="d.object"></my-directive> the scope.data is not undefined anymore, it will show my array of object from resource.
EDIT2: this <my-directive data="d"></my-directive> will also resulting scope.data in my directive got undefined.
EDIT3: Add service code snippet
I think Shomz found the problem. You should use a promise with async call. Like this:
angular.module('module').controller('ModuleController', ['$scope', 'MyService',
function($scope, MyService) {
$scope.data = [];
MyService.myGetRequest().$promise.then(function(data) {
$scope.data = data;
});
}
});

Calling functions between directives

I am currently building my very first APP using angular and I find myself a bit lost, will appreciate any help regarding the following topic.
I have a single page app with two panels. The left side panel displays a list of items (let's say news), and the right side panel displays the details of any of the available news. I want to be able to click on any of the items listed on the left panel and read the details on the right panel.
A sample HTML/JS code for this model will be as followed (This code will not work as it is):
HTML:
<div id="leftPanel">
<news-list></news-list>
</div>
<div id="rightPanel">
<news-detail></news-detail>
</div>
Javascript/Angular:
var app = angular.module('app', []);
app.directive('newsList', function () {
return{
restrict: 'E',
templateUrl: 'templates/news-list.html',
controller: function ($show) {
$scope.clickAction(item){
data = ....some JSON http request...
showDetail(data);
}
},
controllerAs: 'listing'
};
});
app.directive('newsDetail', function () {
return{
restrict: 'E',
templateUrl: 'templates/news-detail.html',
controller: function ($scope) {
$scope.showDetail(data){
....
}
},
controllerAs: 'detail'
};
});
As you can see, the showDetail() function from newsDetail has to be called from the newsList directive controller above. The click action event is called from the different elements within the news list template.
The question is: ¿How can I call a function within a controller from another controller when inside a directive?
Thanks
It would probably be a better idea for you to declare a controller in a parent element of both newsList and newsDetail . Here's my suggestion:
var app = angular.module('app', []);
app.directive('newsList', function () {
return{
restrict: 'E',
templateUrl: 'templates/news-list.html',
controller: function ($show) {
$scope.clickAction(item){
data = ....some JSON http request...
$scope.showDetail(data);
}
},
controllerAs: 'listing'
};
});
app.directive('newsDetail', function () {
return{
restrict: 'E',
templateUrl: 'templates/news-detail.html',
controllerAs: 'detail'
};
});
app.controller('newsCtrl', function($scope){
$scope.showDetail = function(data){
....
};
});
And nest your directives in the parent element on which you use the ngController directive:
<div ng-controller="newsCtrl">
<div id="leftPanel">
<news-list></news-list>
</div>
<div id="rightPanel">
<news-detail></news-detail>
</div>
</div>
How about trying to add the directive you wanna associate with inside your directive as follows:
var app = angular.module('app', []);
app.directive('newsList', function () {
return{
required: 'newsDetail',
restrict: 'E',
templateUrl: 'templates/news-list.html',
controller: function ($show) {
$scope.clickAction(item){
data = ....some JSON http request...
detail.showDetail(data);
}
},
controllerAs: 'listing'
};
});
app.directive('newsDetail', function () {
return{
restrict: 'E',
templateUrl: 'templates/news-detail.html',
controller: function ($scope) {
$scope.showDetail(data){
....
}
},
controllerAs: 'detail'
};
});

Resources