I created a directive for google map auto-complete. everything is working fine, but the problem is when I need to access the value of input and re-set it. it doesn't work. Here is code:
<div controller='mainCtr'>
<span click='reset(destination)'>Reset</span>
<div class='floatleft' style='width:30%;margin-right:40px;'>
<smart-Googlemaps locationgoogle='destination.From'></smart-Googlemaps>
<label>From</label>
</div>
</div>
In the directive:
angular.module('ecom').directive('smartGooglemaps', function() {
return {
restrict:'E',
replace:false,
// transclude:true,
scope: {
locationgoogle: '='
},
templateUrl: 'components/directives/autocomplete/googlemap-search.html',
link: function($scope, elm, attrs){
var autocomplete = new google.maps.places.Autocomplete($(elm).find("#google_places_ac")[0], {});
google.maps.event.addListener(autocomplete, 'place_changed', function() {
var place = autocomplete.getPlace();
// $scope.location = place.geometry.location.lat() + ',' + place.geometry.location.lng();
// console.log(place);
$scope.locationgoogle = {};
$scope.locationgoogle.formatted_address = place.formatted_address;
$scope.locationgoogle.loglat = place.geometry.location;
$scope.locationgoogle.locationText = $scope.locationText;
$scope.$apply();
});
}
}
})
Here is html for directive:
<input id="google_places_ac" placeholder="Please enter a location" name="google_places_ac" type="text" class="input-block-level" ng-model='locationText'/>
The directive works fine, I create a isolated scope(locationgoogle) to pass the information I need to parent controller(mainCtr), now in the mainCtr I have a function calld reset(), after I click this,I need to clean up the input make it empty. How Can I do it?
One way to access the value of the model in your directive from a parent controller is to put that on the isolate scope too and use the two-way binding flag = like you've done with the locationgoogle property. Try this:
DEMO
html
<body ng-controller="MainCtrl">
<button ng-click="reset()">Reset</button>
<smart-googlemaps location-text="locationText"></smart-googlemaps>
</body>
js
app.controller('MainCtrl', function($scope) {
// need to define model in parent and pass to directive
$scope.locationText = {
value: ''
};
$scope.reset = function(){
$scope.locationText.value = '';
}
});
app.directive('smartGooglemaps', function() {
return {
restrict:'E',
replace:false,
// transclude:true,
scope: {
locationgoogle: '=',
locationText: '='
},
// ng-model="locationText.value"
template: '<input id="google_places_ac" placeholder="Please enter a location" name="google_places_ac" type="text" class="input-block-level" ng-model="locationText.value"/>',
link: function($scope, elm, attrs){
// implement directive googlemaps logic, set text value etc.
$scope.locationText.value = 'foo';
}
}
})
Related
I have a directive, with an attribute :
html :
<my-directive id="test" myattr="50"></my-directive>
js :
myApp.directive('myDirective', function() {
var link = function(scope, element, attrs) {
scope.$watch('myattr', function(value) {
element.attr('myattr', value);
});
scope.change = function() {
// some code
};
};
return {
restrict: 'E',
template: '<input type="text" ng-change="change()" ng-model="myattr"/>',
scope: {myattr: '='},
link: link
};
});
My goal would be to keep myattr and the value of the input equal. With element.attr('myattr', value) I can force myattr to have the correct value, but how am I supposed to update the input when myattr changes?
For example, in this jsfiddle, when clicking on the button, I try to do :
$('#test').attr('myattr', Math.random() * 100);
But I can't find a way to 'catch' the change from within the directive.
I would like some help modifying the jsfiddle so that :
the function change is called after the jquery call.
the value of the input is always equal to myattr
You need to store the value of myattr as a property on a scope not as a value on an attribute.
Your directive is correct you need to also attach a controller.
var myApp = angular.module('myApp', []);
myApp.controller('MainController', function ($scope) {
$scope.calculate = function () {
// your logic here
alert($scope.val);
}
});
myApp.directive('myDirective', function() {
var link = function(scope, element, attrs) {
scope.change = function() {
console.log("change " + scope.myattr);
};
};
return {
restrict: 'E',
template: '<input type="text" ng-change="change()" ng-model="myattr"/>',
scope: {
myattr: '='
},
link: link
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="MainController">
My Value: {{val}} <br/>
<button type="button" ng-click="calculate()">ok</button>
<my-directive id="test" myattr="val"></my-directive>
</div>
</div>
So I'm beginner to angularjs and firebase and I'm trying to develop an app which adds values(numerical) on an input. So far I have this:
app.js:
var app = angular.module("app", ['firebase']);
app.directive('addOne', function() {
return {
link: function(scope,element) {
element.bind('click', function() {
console.log(element.parent().find('input'))
element.parent().find('input')[1].value++;
});
}
}
});
and my view:
<section class="form-group">
<label for="">$</label> <input type="button" value="+" add-one>
<input ng-model="user.level" type="text" class="form-control" />
</section>
and my controller:
app.controller('mController', ['$scope', 'User',
function($scope, backHome, User, adicionar){
$scope.user = User(1);
User(1).$bindTo($scope, "user");
}
]);
the thing is that after I click the button with the directive add-one the value of the input changes but the $bindTo is not working...
So why does the bindTo doesn't work when I make a change directly in the DOM?
AngularJS doesn't care what the value of an input is set to, it only cares about what's in the ng-model. Try this...
app.directive('addOne', function() {
return {
link: function(scope,element) {
element.on('click', function() {
scope.$apply(function(){
scope.user.level++
});
});
}
}
});
As pointed out by #PankajParkar, you also need to use scope.$apply when you want to update a binding from event.
angular.module('demo', [])
.controller('DemoController', function($scope){
$scope.user={level: 1};
})
.directive('addOne', function() {
return {
link: function(scope, element, attrs) {
element.on('click', function() {
scope.$apply(scope.user.level++);
});
}
}
})
.directive('unaryInput', function(){
return {
restrict: 'EA',
scope: {
model: "=",
txt: '#buttonText'
},
template: '<input type="text" ng-model="model" /><button>{{txt}}</button>',
link: function(scope, element, attrs) {
if(angular.isDefined(attrs.initialVal)) {
scope.model = attrs.initialVal;
}
element.on('click', function() {
if (attrs.direction === 'decrement') {
scope.$apply(scope.model--);
} else {
scope.$apply(scope.model++);
}
});
}
};
});
<script src="https://code.angularjs.org/1.3.15/angular.min.js"></script>
<div ng-app="demo" ng-controller="DemoController">
<input type="text" ng-model="user.level">
<input type="button" value="+" add-one>
<hr>
<unary-input button-text="Add one" model="user.level" direction="increment"></unary-input>
<unary-input button-text="-" model="user.level" direction="decrement"></unary-input>
<hr>
<unary-input button-text="-" model="user.val" direction="decrement" initial-val="10"></unary-input>
</div>
In AngularJS, you want to change the view by changing the model that it's based on, versus doing it imperatively like you might with a traditional jQuery approach for example (traversing the DOM and incrementing the value).
UPDATE
Okay, so here's a nice reusable version of the (please check the snippet to see it in action).
The template includes both the button and the input. It accepts 4 values that you set as attributes:
button-text: The text you want to show on the button.
model: The model value for the input.
initial-val: The initial value for the input if you don't want to initialize on your controller.
direction: Whether to increment or decrement the values. This one currently accepts a string "decrement" to subtract. If you have no direction set or any other value set in the attribute, it will increment.
So, you would use it like this:
<unary-input button-text="Subtract One" model="user.val" direction="decrement" initial-val="10"></unary-input>
And the directive itself looks like this:
.directive('unaryInput', function(){
return {
restrict: 'EA',
scope: {
model: "=",
txt: '#buttonText'
},
template: '<input type="text" ng-model="model" /><button>{{txt}}</button>',
link: function(scope, element, attrs) {
if(angular.isDefined(attrs.initialVal)) {
scope.model = attrs.initialVal;
}
element.on('click', function() {
if (attrs.direction === 'decrement') {
scope.$apply(scope.model--);
} else {
scope.$apply(scope.model++);
}
});
}
};
});
Browsing around I could find a solution doing the way you said in the comments (two buttons one incrementing and another decrementing) thanks a lot for the help! and here's the final version.
app.directive('unaryInput', function(){
return {
restrict: 'EA',
scope: {
model: "="
},
template: '<input type="text" ng-model="model" /><button ng-click="decrement()">-</button><button ng-click="increment()">+</button>',
link: function(scope, element) {
scope.increment = function() {
scope.model++;
}
scope.decrement = function() {
scope.model--;
}
}
};
});
I use a custom directive to get places from Google API. This directive works like a charm in a controller. But when I want to use it inside a modal, it doesn't work any more. It's a question of scope, but I can't figure out what's exactly happened. Any idea ?
My directive :
'use strict';
angular.module('app').directive('googleplace', function() {
return {
require: 'ngModel',
scope: {
ngModel: '=',
details: '=?'
},
link: function(scope, element, attrs, model) {
var options;
options = {
types: ['address'],
componentRestrictions: {}
};
scope.gPlace = new google.maps.places.Autocomplete(element[0], options);
google.maps.event.addListener(scope.gPlace, 'place_changed', function() {
scope.$apply(function() {
scope.details = scope.gPlace.getPlace();
if (scope.details.name) {
element.val(scope.details.name);
model.$setViewValue(scope.details.name);
element.bind('blur', function(value) {
if (value.currentTarget.value !== '') {
element.val(scope.details.name);
}
});
}
});
});
}
};
});
My modal controller :
modalInstance = $modal.open
templateUrl: "modal.html"
controller: ($scope, $modalInstance) ->
$scope.$watch 'placeDetails', ->
_.forEach $scope.placeDetails.address_components, (val, key) ->
$scope.myaddress = val.short_name + ' ' if val.types[0] is 'street_number'
return
And finally, my html :
<div class="modal-body">
<div>
<input type="text" placeholder="Start typing" ng-model="address" details="placeDetails" googleplace />
</div>
<div>
<input type="text" ng-model="myaddress">
</div>
</div>
I should have the ng-model="address" populated with the result of the call to Google Place API, and the ng-model="myaddress" populated by the $watch, but nothing happens.
Here is my plunkr http://plnkr.co/edit/iEAooKgfUUfxoBWm8mgw?p=preview
Click on "Open modal" causes the error : Cannot read property 'address_components' of undefined
working demo
According to how to create modal in angularjs
Extra things that i added :
1 : New Controller for modal
2 : Blur function that fires on property change Instead of $watch
DEMO
Imagine I have some markup, e.g.:
<my-input model="data.firstName"></my-input>
Now, I would like to create a my-markup directive that will add a button to show/hide its markup.
So, this:
<div my-markup>
<my-input model="data.firstName"></my-input>
</div>
should result in this:
and when the button is clicked, the markup should appear:
The my-markup directive should not break any data bindings of its children.
Here is my attempt to implement this.
The markup appears, but the button doesn't work. Any ideas how to fix this?
PLAYGROUND HERE
Here is my approach. Couple of things:-
1) Instead of isolated scope on myMarkup, create a child scope, ultimately the actual directive myInput will be isolated. This would be required if you do need to support multiple myMarkup directive under the same scope.
2) You need a click event on the button, i wouldn't do logic on the markup instead abstract out to a method on the scope.
3) You would just need one button, do not need 2 buttons. Just change the text of the button.
.directive('myMarkup', function($compile) {
return {
restrict: 'A',
scope: true, //Create a child scope
compile: function(element) {
//Just need one button
var showButton = '<button ng-click="toggleMarkup()">{{model.showMarkup ? "Hide": "Show"}} Markup</button>';
var markup = '<pre ng-show="model.showMarkup">' + escapeHtml(element.html()) + '</pre>';
//append the markups
element.append(showButton).append(markup);
return linker;
}
};
function linker(scope, element) {
scope.model = {
showMarkup: false
};
//Click event handler on the button to toggle markup
scope.toggleMarkup = function(){
scope.model.showMarkup = !scope.model.showMarkup;
}
};
});
Demo
Please see below
function escapeHtml(html) {
return html.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"');
}
angular.module('App', []).controller('AppCtrl', function($scope) {
$scope.data = {
firstName: 'David'
};
}).directive('myInput', function() {
return {
restrict: 'E',
scope: {
model: '='
},
template: '<input class="my-input" type="text" ng-model="model">'
};
}).directive('myMarkup', function() {
return {
restrict: 'A',
scope: {},
link: function(scope, elem, attr) {
},
compile: function(element) {
var showButton = '<button ng-if="data.showMarkup" ng-click="data.showMarkup=!data.showMarkup">Hide Markup</button>';
var hideButton = '<button ng-if="!data.showMarkup" ng-click="data.showMarkup=!data.showMarkup">Show Markup</button>';
var markup = '<pre ng-if="data.showMarkup">' + escapeHtml(element.html()) + '</pre>';
element.append(showButton);
element.append(hideButton);
element.append(markup);
return function(scope, element) {
scope.data = {
showMarkup: true
};
};
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="App" ng-controller="AppCtrl">
<pre>data = {{ data | json }}</pre>
<div my-markup>
<my-input model="data.firstName"></my-input>
</div>
</body>
I am attempting to access the form inside my directive for validation purposes, so I'd like access to $setPristine, however, I can't seem to figure out how to get the form if it's created using a templateUrl.
I have a plunker detailing the issue here: http://plnkr.co/edit/Sp53xzdTbYxL6DAue1uV?p=preview
I'm getting an error:
Controller 'form', required by directive 'testDirective', can't be found!
Here is the relevant Plunker code:
.js:
var app = angular.module("myApp", []);
app.directive("testDirective", function() {
return {
restrict: 'E',
scope: {},
templateUrl: "formTemplate.html",
require: "^form", // <-- doesn't work
link: function (scope, element, attrs, ctrl) {
console.log(ctrl);
scope.open = function() {
// Would like to have access to the form here
// ctrl.$setPristine();
}
},
controller: function($scope) {
$scope.firstName = "Mark";
$scope.save = function(form) {
console.log(form);
}
}
}
})
formTemplate.html:
<form name="testForm" ng-click="save(testForm)">
<input type="text" ng-model="firstName" />
<br>
<input type="submit" value="Save" />
</form>
How can I attach the form in formTemplate.html to the isolated scope of my directive?
http://plnkr.co/edit/41hhRPKoIsZ9C8Y9Yi87?p=preview
Try this in your directive:
var form1 = element.find('form').eq(0);
formCtrl = form1.controller('form');
console.log(formCtrl);
this should grab the controller for the form.