Angular service into directive through ng-repeat - javascript

I'm trying to make a custom directive to create a menu where the href value changes depending on the family.id value which I got via service.
I'm trying to use that family.id value of each element in ng-repeat to query a new value through a service and change the href depending on that value
product.html
<ul class="list" custom-href categories="getfamilies"></ul>
href.js
(function(){
var app = angular.module('hrefDirective',['productsService']);
app.directive('customHref',['productsService', function(productsService) {
return {
restrict: 'EA',
transclude: false,
scope: {
categories: '='
},
template: '<li ng-repeat="family in categories"><p><a ng-bind="family.name" ng-href="{{newHref}}"></a></p></li>',
link : function($scope,$element, $attrs){
productsService.finalFamilies(family.id).then(function(data){
if(data == 0){
$scope.newHref = '#/product/';
}else{
$scope.newHref = '#/product/{{family.id}}';
}
});
}
};
}]);
})();
products.js
(function(){
var app = angular.module('productosController',['productsService']);
app.controller('ProductsFamiliesController', function($scope, productosService){
productsService.getFamilies().then(function(data){
$scope.getfamilies = data.data;
});
});
})();
services.js
(function(){
var app = angular.module('productsService',[]);
app.factory('productsService', ['$http','$location', function($http, $location){
var webServiceUrl = 'my url'
var products = [];
products.getFamilies = function(){
return $http.get(webServiceUrl+'getFamilies');
};
products.finalFamilies = function(idFamily){
return $http.get(webServiceUrl+'getFinalFamiles?id'+ idFamily);
};
return products;
}]);
})();
Of course is not working...any clues...?
plnkr.co/edit/3N19E0KyeWW7wvsLOlkk?p=info

Related

AngularJS calling a controller in a directive

I am trying to follow from what I can tell so far is a pretty decent tutorial but I am a little stuck on a part where I need to create a directive to seperate a chunk of html and use a controller to generate the data.
var app = angular.module('newModule',[]);
app.directive('stateView', function(){
return{
restrict: 'E',
templateUrl: 'state-view.html',
controller: 'stateController',
controllerAs: 'stateCtrl'
}
});
app.controller('stateController',function(){
this.addStateTo = function(country){
if(!country.states){
country.states = [];
}
country.states.push({name: this.newState});
this.newState = "";
};
});
My HTML stateview looks like this (C is a value from another controller to itterate through a list of other objects).
<div>
<input type="text" name="state" ng-model="stateCtrl.newState">
<a href ng-click="stateCtrl.addStateTo(c)"> Add State {{ stateCtrl.newState }}</a>
</div>
and the only HTML referrence I have on my index is the following:
<state-view></state-view>
It looks clean, but the problem is that it does not reconize the function addStateTo unless I tell the DIV element that it is the ng-controller called StateController. Isn't this what the directive is telling the HTML attribute?
You are using the ControllerAs syntax and referencing the controller context appropriately (i.e. stateCtrl.newState and stateCtrl.addStateTo(c)). The problem is that you aren't creating the controller context properly. Your controller code should look like this:
app.controller('stateController', function() {
var vm = this;
this.addStateTo = function(country) {
if (!country.states) {
country.states = [];
}
country.states.push({
name: vm.newState
});
vm.newState = "";
};
});
Working example here
Try this instead ($scope instead of this):
app.controller('stateController',function($scope){
$scope.addStateTo = function(country){
if(!country.states){
country.states = [];
}
country.states.push({name: this.newState});
$scope.newState = "";
};
});
OR
app.controller('stateController',function(){
var vm = this;
vm.addStateTo = function(country){
if(!country.states){
country.states = [];
}
country.states.push({name: this.newState});
vm.newState = "";
};
});
Try adding bindto controller true in your directive. And also the above answer is correct in fixing other issues you may run into, i.e mapping your this to the function, though at present not doing that may not cause a problem.
var app = angular.module('newModule',[]);
app.directive('stateView', function(){
return{
restrict: 'E',
templateUrl: 'state-view.html',
controller: 'stateController',
controllerAs: 'stateCtrl',
bindToController: true
}
});
app.controller('stateController',function(){
var vm = this;
vm.addStateTo = function(country){
if(!country.states){
country.states = [];
}
country.states.push({name: vm.newState});
vm.newState = "";
};
});

how to bind a directive var to controller

i have a problem with using 2 way binding in angular, when i change my input, the change dosnt affect to controller. but the first init from controller affect directive.
in the picture i changed the value, but vm.date still have value test.
my directive:
(function (app) {
app.directive('datePicker', function () {
//Template
var template = function (element, attrs) {
htmltext =
'<input ng-readonly="true" type="text" id="' + attrs.elementId +
'" ng-model="' + attrs.model + '" type="date" />';
return htmltext;
}
//Manipulation
var link = function ($scope, elements, attrs, ctrls) {
//Declare variables we need
var el = '#' + attrs.elementId + '';
var m = attrs.model;
var jdate;
var date;
$scope[attrs.model] = [];
$(el).on('change', function (v) {
jdate = $(el).val();
gdate = moment(jdate, 'jYYYY/jMM/jDD').format('YYYY-MM-DD');
if (moment(gdate, 'YYYY-MM-DD', true).isValid()) {
date = new Date(gdate);
$scope[m][0] = date;
$scope[m][1] = jdate;
//console.log($scope[m]);
$scope.vm[m] = $scope[m];
console.log($scope.vm); //----> Here Console Write Right Data
} else {
//console.log('Oh, SomeThing is Wrong!');
}
});
} // end of link
return {
restrict: 'E',
scope: {vm: '='},
template: template,
link: link
};
});
}(angular.module('app')));
and my controller:
(function (app) {
app.controller('test', ['$scope', function ($scope) {
var vm = this;
vm.date = 'test';
vm.mydate = 'test2';
}]);
}(angular.module('app')));
and html:
<body ng-app="app">
<div ng-controller="test as vm">
<date-picker element-id="NN" model="vm.date" vm="vm"></date-picker>
<p>{{vm.date}}</p>
<date-picker element-id="NN2" model="vm.mydate" vm="vm"></date-picker>
<p>{{vm.mydate}}</p>
</div>
</body>
I am not sure why you made the textbox as readonly, but if you remove that readonly and try to update the textbox then the two way binding works. Here's the fiddle for that
https://fiddle.jshell.net/dzfe50om/
the answer:
Your controller has a date property, not a vm.date property. – zeroflagL May 25 at 13:48
You should define vm to $scope instead of this;
var vm = $scope;

My ng-model is not updating first time

i am having a problem with ng-model .When i am adding tag from suggestion list its not updating model value until i am not deleting tags and adding again.In my project its working fine but for plunker only its happening.Please check this out and help me..
thank you..
Here is my html:-
<tags-input ng-model="tags2" display-property="tagName" on-tag-added="getTags()" id="target">
<auto-complete source="loadTags($query)" min-length="2"></auto-complete>
</tags-input>
<p>{{tags2}}</p>
Here is my js:-
var app = angular.module('myApp', ['ngTagsInput', 'ui.bootstrap']);
app.controller(
'myController',
function($scope, $http) {
$scope.tagsValues =[];
$scope.loadTags = function(query) {
return $http.get('tags.json');
};
$scope.getTags = function() {
$scope.tagsValues = $scope.tags2.map(function(tag) {
return tag.tagId;
});
alert(" Tag id is :"+ $scope.tagsValues);
};
});
Here is my plunker:-
http://plnkr.co/edit/6Mr2qk2S2RvGJLevf2UI?p=preview
All you have to do to make this work, is define and initialize the variable $scope.tags2 first:
var app = angular.module('myApp', ['ngTagsInput', 'ui.bootstrap']);
app.controller(
'myController',
function($scope, $http) {
$scope.tagsValues = '';
$scope.tags2 = [];
$scope.loadTags = function(query) {
return $http.get('tags.json');
};
$scope.getTags = function() {
$scope.tagsValues = $scope.tags2.map(function(tag) {
return tag.tagId;
});
alert(" Tag id is :"+ $scope.tagsValues);
};
});
See my plunker: http://plnkr.co/edit/Y3f8kLBnexOXg5gwmldL?p=preview

AngularJS directive - template from $scope with other directives

A bit confused with binding...
How to properly bind values from input fields to textarea?
app.controller('MainCtrl', function($scope) {
$scope.hello = 'Hello';
$scope.world = 'World!'
//this template comes from json
$scope.fromjson = "{{hello}} {{world}} and have a good time";
//this template comes from json
});
And a simple body:
<body ng-controller="MainCtrl">
<input ng-model="hello">
<input ng-model="world">
<helloworld></helloworld>
</body>
A have to edit my miserable example because your
kindly answers didn't solve my problem.
I had plenty of unique texts - letter templates in which some fields should be filled by user. There are ten fields occuring conditionally depending of text selected.
text1: "Blah, blah {{field.first}}.blah {{filed.second}}"
text2: "{{field.third}} blah, blah {{field.first}}"
text3: "Blah, blah {{field.fourth}}"
and so on...
Texts are stored in database and obtained through JSON
function(textid) {
$http.get('json/json.php',
{ params: { id: textid } }).
success(function(data, status, headers, config) {
$scope.SelectedText = data;
})
};
I organized it in one form with all ten input fields, visible depending of
selected text.
Completed/filled template should be visible in textarea at the bottom of form to be copied to another place.
Should I change the way I store the templates?
or back to question is there any other way the fields could be inserted into view ?
I think what you need is $interpolate service and $scope.$watch take a look at this jsfiddle :
http://jsfiddle.net/michal_taborowski/6u45asg9/
app.controller('MainCtrl', function($scope,$interpolate) {
$scope.hello = 'Hello';
$scope.world = 'World!';
//this template comes from json
$scope.template = " {{hello}} {{world}} and have a good time";
//this template comes from json
var updateTemplate = function(oldVal,newVal,scope){
scope.fromjson = $interpolate(scope.template)(scope);
}
$scope.$watch('hello', updateTemplate );
$scope.$watch('world', updateTemplate );
});
Of course you should move $watch to link function in your directive and pass hello and world as scope variable to this directive - this is just a quick example how you can do it.
I think that what you want is this:
app.controller('MainCtrl', function($scope) {
$scope.hello = 'Hello';
$scope.world = 'World!'
//this template comes from json
$scope.fromjson = function(){
return $scope.hello + " " + $scope.world + " and have a good time";
};
});
app.directive('helloworld', function() {
return {
restrict: 'E',
template: '<textarea>{{fromjson()}}</textarea>'
};
});
Example here: http://plnkr.co/edit/8YrIjeyt9Xdj2Cf7Izr5?p=preview
The problem with your code is that when you declare $scope.fromjson = "{{hello}} {{world}} and have a good time" you are not binding anything, you are just assiging that string to the fromjson property.
EDIT:
As HeberLZ pointed out in the comment bellow, it would be much more efficient to do this instead:
app.controller('MainCtrl', function($scope) {
$scope.hello = 'Hello';
$scope.world = 'World!'
});
app.directive('helloworld', function() {
return {
restrict: 'E',
template: '<textarea>{{ hello + " " + world + " and have a good time"}}</textarea>'
};
});
One way would be something like this:
Controller:
app.controller('MainCtrl', function($scope) {
$scope.hello = 'Hello';
$scope.world = 'World!'
});
Directive:
app.directive('helloworld', function($http) {
return {
restrict: 'E',
scope: {
'hello': '=',
'world': '='
},
link: function(scope){
scope.jsonFromServer = '';
$http.get('someUrl').then(function(response){
scope.jsonFromServer = response.data;
});
var updateFromjson = function(){
scope.fromjson = scope.hello + ' ' + scope.world + ' ' + scope.jsonFromServer;
}
scope.$watch('hello', updateFromjson);
scope.$watch('world', updateFromjson);
}
template: '<textarea>{{fromjson}}</textarea>'
};
});
Body:
<body ng-controller="MainCtrl">
<input ng-model="hello">
<input ng-model="world">
<helloworld hello="hello" world="world"></helloworld>
</body>
app.controller('MainCtrl', function($scope) {
$scope.hello = 'Hello';
$scope.world = 'World!'
//this template comes from json
$scope.aDiffFunc = function() {
return $scope.hello + " " + $scope.world + " and have a good time";
};
//this template comes from json
});
app.directive('helloworld', function() {
return {
restrict: 'E',
template: '<textarea>{{aDiffFunc()}}</textarea>'
};
});
this should be it??
http://plnkr.co/edit/ygA4U0v7fnuIbqAilrP7?p=preview

AngularJS - Issue appending HTML to single item in ng-repeat

THE SITUATION:
I am exploring AngularJS by re-building a former project with it. I am using the angularjs-rails gem version 1.2.16. I have a page were I make an API call that returns an array of objects of music events. CONTROLLER:
d2jiveControllers.controller('VenueResultsCtrl', ['$scope','$http','$routeParams',
'$compile', '$sce', 'spotifyFactory', function($scope, $http, $routeParams,
$compile, $sce, spotifyFactory){
"use strict";
var venueId = $routeParams.venueId;
var baseUrl = 'http://api.songkick.com/api/3.0/venues/';
var apiKey = '/calendar.json?apikey=************';
var url = baseUrl + venueId + apiKey + '&jsoncallback=JSON_CALLBACK' ;
var init = function(url){
$http.jsonp(url)
.success(function (data) {
$scope.events = data.resultsPage.results.event;
console.log(data);
}).
error(function(){
console.log('failure');
});
};
$scope.tracks = function(artistName){
var artistTracks = spotifyFactory.getArtistTracks(artistName);
var spotifyIframe = $('spotifyIframe');
$scope.show_tracks = $sce.trustAsHtml("<iframe src='https://embed.spotify.com/?uri=spotify:trackset:Playlist:"+artistTracks.spotifyTracks + "'"+
"&theme=white'width='300' height='300'frameborder='0' allowtransparency='true'></iframe>")
console.log(artistTracks)
};
init(url);
}]);
I list them out using ng-repeat and attach an ng-click to each listing. HTML TEMPLATE:
<div class="eventContainer row" ng-controller="VenueResultsCtrl">
<div ng-repeat="event in events">
<h4>
{{event.displayName}}
</h4>
<p>
Buy Tickets
</p>
<div ng-repeat="artist in event.performance">
<button ng-click="tracks(artist.displayName)">Discover
{{artist.displayName}}<br> -- {{artist.billing}}</button><br><br>
<div ng-bind-html="show_tracks"></div>
</div>
</div>
</div>
On click I want to make another API call to Spotify to get back track IDs that I then place into an iframe. To do this I tried both making the call in a directive and factory:
DIRECTIVE:
d2jive.directive('getSpotifyTracks', [function () {
// <div get-spotify-tracks="artist.displayName"></div>
var spotifyUrl = "http://ws.spotify.com/search/1/track.json?callback=JSON_CALLBACK&q=";
return {
restrict: 'AEC',
scope: {
artistName: '='
},
templateUrl: 'assets/d2jive/templates/artistTracks.html',
controller: ['$scope', '$http', function($scope, $http){
$scope.getTracks = function(artistName){
$http.jsonp(spotifyUrl + encodeURIComponent(artistName))
.success(function (data) {
var trackArray = [];
var tracks = data.tracks.slice(0,9);
for (var track in tracks){
grabbedTrack = tracks[track].href.slice(
14, tracks[track].href.length);
trackArray.push(grabbedTrack);
}
$scope.artistTracks = trackArray;
console.log(data);
});
};
}],
link: function(scope, element, attrs, ctrl){
scope.$watch('artist.displayName', function(displayName){
if (displayName){
scope.getTracks(displayName);
}
})
}
}
}]);
FACTORY::
d2jive.factory('spotifyFactory', ['$http','$q', function($http, $q){
var factory = {}
factory.getArtistTracks = function(artistName){
var tracks = {}
var spotifyUrl = "http://ws.spotify.com/search/1/track.json?q=";
var deferred = $q.defer();
var getTracks = function(artistName){
$http.get(spotifyUrl + encodeURIComponent(artistName))
.success(function (data) {
deferred.resolve(data);
});
return deferred.promise;
};
// tracks.spotifyTrakcs = getTracks(artistName);
var spotifyTracks = getTracks(artistName);
spotifyTracks.then(function(result){
var trackArray = [];
var tracks = result.tracks.slice(0,9);
for (var track in tracks){
grabbedTrack = tracks[track].href.slice(
14, tracks[track].href.length);
trackArray.push(grabbedTrack);
}
tracks.spotifyTracks = trackArray;
});
return tracks;
}
return factory;
}]);
THE PROBLEM:
I can't find a way to append the iframe HTML to a particular item and not each event that is listed. The directive didn't seem to work because it loaded right away and slowed down the app way too much. That is why I went with a Factory to make the API call to Spotify and append the iframe.
THE GOAL:
On ng-click make API call to Spotify, return the track ID's, insert them into the iframe, and then insert that right below the clicked item not below all of the items.
Any help will be much appreciated! Thanks.
Inside the $watch in the link function of your directive, return early on equality between newVal and oldVal parameters:
link: function(scope, element, attrs, ctrl){
scope.$watch('artist.displayName', function(displayName, oldVal){
if (displayName === oldVal) { return }
if (displayName){
scope.getTracks(displayName);
}
})
}
That should prevent getTracks() from being called as soon as the directive links.

Categories

Resources