how to bind a directive var to controller - javascript

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;

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 = "";
};
});

Input field not being updated from controller in Angularjs

I am in learning phase of Angularjs and am stuck in a problem for last two days. I have seen lots of answer but don't know how to adapt those solutions in my case. What I want to do is update the input field via buttons using angularjs.
// html
<body ng-controller="Controller">
<input type="number" ng-model="data" update-view>
<br>
<label for="data">{{data}}</label>
<button name="btn1" ng-click='updateInput(1)'>1</button>
</body>
// js
var app = angular.module('calculator',[]);
app.controller('Controller', function($scope, $timeout){
$scope.data = 0;
var val = '';
$scope.updateInput = function(param) {
val += String(param);
$scope.data = val;
// val = param;
// $scope.data = val;
}
});
The expressions gets evaluated but the input field is not updating. I have seen other updating views with $setViewValue and $render but I don't know how to use them here.
app.directive('updateView', function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
element.bind('change', function () {
// console.log(ngModel);
scope.$apply(setAnotherValue);
});
function setAnotherValue() {
ngModel.$setViewValue(scope.data);
ngModel.$render();
}
}
};
});
Any help would be appreciated. Thanks
You don't need a directive for updating.
You seem to be setting a string value to $scope.data, which throws an error, because the input type is number.
angular.module('calculator', [])
.controller('Controller', function($scope){
$scope.data = 0;
var val = '';
$scope.updateInput = function(n){
val = n;
$scope.data = val;
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
<body ng-app="calculator" ng-controller="Controller">
<input type="number" ng-model="data">
<button ng-click="updateInput(1)">1</button>
</body>
I was noting that without converting the parameter into string, the input field would update with the changed model but as soon as I would change it into String, it would not update the input field. Also there was error thrown in console. So I just, on hit and trial basis, converted it back to int by changing only one piece of line $scope.data = val; into $scope.data = parseInt(val, 10); and hurrrayyy the input field is all updating just like I wanted. And as #cither suggested, I don't need to directive for this. Following is my working code
var app = angular.module('calculator',[]);
app.controller('Controller', function($scope, $timeout){
$scope.data = 0;
var val = '';
$scope.updateInput = function(param) {
val += String(param);
$scope.data = parseInt(val, 10);
}
});

Angular service into directive through ng-repeat

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

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