What Scope is being used in Angular's $mdDialog - javascript

I have been playing around with Angular Material and I wanted to create a $mdDialog that allows a user to enter in information that when saved, will update an object tied to a ng-repeat.
While trying to get this to work and trying out different parameters for mdDialog.show() I was confused about what scope is being used when/why.
This is the first implementation:
(function () {'use strict';
angular.
module('myApp', ['ngMaterial']).
controller('AppCtrl', AppCtrl);
function AppCtrl($mdDialog, $scope) {
$scope.lister = [{name:'Matt'},{name:'Steve'}];
$scope.showDialog = showDialog;
function showDialog(evt) {
$scope.obj = {name:'default'};
$mdDialog.show({
targetEvent: evt,
scope: $scope.$new(),
template:
'<md-dialog>' +
' <md-content><md-input-container>'+
' <label>Name</label>'+
' <input ng-model="obj.name">' +
' </md-input-container></md-content>' +
' <div class="md-actions">' +
' <md-button ng-click="close(obj)">' +
' Save' +
' </md-button>' +
' </div>' +
'</md-dialog>'
}).then(function(objs){$scope.lister.unshift(objs)});
}
$scope.close = function(objs){
$mdDialog.hide(objs);
}
}
}());
The behavior of the above code is mdDialog will open with "default" in the Name input field, but if I change the show() parameters to below (key difference is swapping out "scope:" for "controller:"):
function showDialog(evt) {
$scope.obj = {name:'default'};
$mdDialog.show({
targetEvent: evt,
controller: 'AppCtrl',
template:
'<md-dialog>' +
' <md-content><md-input-container>'+
' <label>Name</label>'+
' <input ng-model="obj.name">' +
' </md-input-container></md-content>' +
' <div class="md-actions">' +
' <md-button ng-click="close(obj)">' +
' Save' +
' </md-button>' +
' </div>' +
'</md-dialog>'
}).then(function(objs){$scope.lister.unshift(objs)});
}
The behavior of the second implementation is that the mdDialog will open with a blank for the Name input field.
This is a long setup for this question: Why does the dialog template recognize $scope.obj when "scope: $scope.$new()", but it is not recognized when "controller: 'AppCtrl'"? I thought both implementations are providing AppCtrl's scope to the dialog.

Dialog is always given an isolated scope
You can pass data to dialog from parent controller using dependency injection.
function AppController($scope, $mdDialog) {
var message='message from parent';
$scope.showDialog = showDialog;
$scope.items = [1, 2, 3];
function showDialog($event) {
var parentEl = angular.element(document.body);
$mdDialog.show({
parent: parentEl,
targetEvent: $event,
templateUrl:'templateFile.html',
locals: {
items: $scope.items
},
message:message
controller: DialogController
});
function DialogController($scope, $mdDialog, items,message) {
$scope.items = items;
$scope.message = message;
$scope.closeDialog = function() {
$mdDialog.hide();
}
}
}
In your first case, you are adding an object to isolated scope of your dialog using
$scope.obj = {name:'default'} and its available as obj.name on yr view.
In your second case, you are declaring controller for your dialog as 'AppCtrl', but you have not defined it anywhere inside your parent controller, so you are not getting anything on view. AppCtrl is not defined.

if you want to use calling scope; you can pass 'isolateScope' parameter like this:
$mdDialog.show({
....,
isolateScope: false
});

Related

How to call Javascript Function in Angular Js on click Event

I am trying to call Javascript Function in Angular Js on click Event..
Here is the code for click event
listData += '<div class="col-sm-8 padding-left-0 word-wrap"><span class="pros-desc">' + desc + '</span>...<span></span><a href="#" class="redmre ProfilePreview" attr-Location='
+ data.FirmLocation + ' attr-Urlkey=' + data.Urlkey + ' value=' + data.FirmID + ' attr-firm="' + data.FirmName + '" attr-LoginId="'
+ data.LoginId + '">View Profile</a></div><div class="col-sm-1"></div><div class="col-md-4 gap-10"><button type="button" class="bttn btn-green-md btn-BTsm btn-block" onclick="mymodel(' + user + ',' + firm + ')" id="myBtn">GET IN TOUCH</button></div>';
and here is Javascript function ..
function mymodel(user, firm) {
id = user;
name=firm;
}
and here is angular js code
var app = angular.module('myApp', []);
app.controller("customersCtrl", function ($scope, $http, $window) {
var a; // user value must come over here
var b; // firm value must come over here
}
I need user and firm value of Javascript function in my angular js controller but it's not working.
I don't have any other option like I can convert all code in Javascript or angular js, it's small piece of large code so I don't want to change anything else.
I have tried so many options but still it is not working so if anyone can help me out that will be great.
<div ng-click="mymodel(user, firm)"></div>
var app = angular.module('myApp', []);
app.controller("customersCtrl", function ($scope, $http, $window) {
$scope.mymodel = function(a,b){
//do whatever you want to do with values..
}
}

ng-switch in custom angular directive breaks two way binding

I created my custom directive to encapsulate an uib-datepicker-popup:
'use strict';
angular.module( 'frontendApp' )
.directive( 'inputDate', function(){
var controller = function(){
var vm = this;
function init() {
vm.formats = [ 'dd.MMMM yyyy', 'yyyy/MM/dd', 'dd.MM.yyyy', 'shortDate' ];
vm.format = vm.formats[ 0 ];
vm.altInputFormats = [ 'M!/d!/yyyy' ];
vm.dateOptions = {
datepickerMode: 'day',
formatYear: 'yy',
maxDate: new Date(),
minDate: new Date( 1900, 1, 1 ),
startingDay: 1
};
vm.datepicker = {
opened: false
};
};
init();
vm.showDatePicker = function(){
vm.datepicker.opened = true;
};
};
var template = '<div ng-switch on="readonly" >' +
'<div ng-switch-when="true" class="form-control" readonly>' +
'<div readonly name="readonlyText">{{ngModel | date : \'d.MMMM yyyy\'}}</div>' +
'</div>' +
'<div ng-switch-default class="input-group">' +
'<input class="form-control" type="text" uib-datepicker-popup="{{vm.format}}" ng-model="ngModel" ng-model-options="{timezone:\'UTC\'}" is-open="vm.datepicker.opened" datepicker-options="vm.dateOptions" ng-required="true" show-button-bar="false" alt-input-formats="vm.altInputFormats" />' +
'<span class="input-group-btn">' +
'<button type="button" class="btn btn-default" ng-click="vm.showDatePicker()"><i class="glyphicon glyphicon-calendar"></i></button>' +
'</span>' +
'</div>' +
'</div>';
return{
controller: controller,
controllerAs: 'vm',
bindToController: true,
template: template,
restrict: 'EA',
scope :true,
require:'ngModel',
link: function( scope, element, attrs, ngModel ){
// Bring in changes from outside:
scope.$watch( 'ngModel', function(){
if( ngModel ) {
scope.$eval( attrs.ngModel + ' = ngModel' );
}
} );
// Send out changes from inside:
scope.$watch( attrs.ngModel, function( val ){
if( val ) {
scope.ngModel = val;
}
} );
if( attrs.readonly === 'true' ) {
scope.readonly = true;
}
}
};
} );
The html part then is:
<input-date ng-model="form.flight.date"></input-date>
The problem: if the popup shows up, scope.ngModel is initialized correctly from attrs.ngModel. I had a log inside the watcher that showed me that watching attrs.ngModel works perfecly, but watching 'ngModel' or scope.ngModel does only work until i use the datepicker. It works perfectly as long as the datepicker is not triggered.
Just discovered that it works perfectly if i remvoe the
"ng-switch-default". Replacing it with ng-show/ng-hide makes the directive work completely as expected.
Can anyone explain why?
The behavior you see is absolutely correct. When you use structural directives like ng-if, ng-switch, ng-repeat etc. it creates a new scope and copies all attributes of the parent scope. Your model is a primitive (string), so it is fully copied to the new scope and changed within this scope without propagation to the parent one.
What you can do is:
Use object instead of string to pass the ng-model, what I personally find here very awkward
Use ng-model from controller object and not from the scope
Going on with the second approach: you already use bindToController and an isolated scope by scope: true, so just instead of tracking the model with watcher bind it to the controller:
return {
bindToController: true,
scope: {
ngModel: '='
},
...
so ideally you won't even need your link function and in the template instead of
'<div readonly name="readonlyText">{{ngModel | date : \'d.MMMM yyyy\'}}</div>'
use
'<div readonly name="readonlyText">{{vm.ngModel | date : \'d.MMMM yyyy\'}}</div>'
Why ng-hide still works? It does not create a new scope.

angularjs - $destroy isn't called on custom compiled html

I made a directive that compiles a 'div' with an ng-include and ng-controller, and appends it to the element. It works fine, but for some reason the "$destroy" event isn't called on the controller's scope when it is destroyed.
Is what I'm doing correct?
This is the html:
<my-Directive tr-model="someBindedObject"></my-Directive>
And this is the JS:
angular.module('myModule').
directive('myDirective', function ($compile, controllerMapper) {
function compileTemplate(scope, element, attrs) {
var controller = controllerMapper.getController(scope.model);
innerHtml = '<div ng-include src="' + "'" + controller.templateUrl + "'" + '" ng-controller="' + controller.controllerName + '" ></div>';
var innerElement = $compile(innerHtml)(scope);
element.empty();
element.append(innerElement);
};
return {
scope: {
model: "=trModel"
},
link: compileTemplate
}
}).
controller('myController', function($scope){
$scope.$on('$destroy', function () {
//this is never called
});
});

Is the way of creating a directive scope the same in versions 1.3 and 2 of AngularJS?

I have the following directive that my application is using. I was under the impression that my application was working fine with AngularJS 1.3 but after a lot of changes including a move to the latest version, the removal of jQuery, and also the use of controller as then now this directive is giving me errors:
app.directive('pagedownAdmin', function ($compile, $timeout) {
var nextId = 0;
var converter = Markdown.getSanitizingConverter();
converter.hooks.chain("preBlockGamut", function (text, rbg) {
return text.replace(/^ {0,3}""" *\n((?:.*?\n)+?) {0,3}""" *$/gm, function (whole, inner) {
return "<blockquote>" + rbg(inner) + "</blockquote>\n";
});
});
return {
require: 'ngModel',
replace: true,
scope: {
modal: '=modal'
},
template: '<div class="pagedown-bootstrap-editor"></div>',
link: function (scope, iElement, attrs, ngModel) {
var editorUniqueId;
if (attrs.id == null) {
editorUniqueId = nextId++;
} else {
editorUniqueId = attrs.id;
}
var newElement = $compile(
'<div>' +
'<div class="wmd-panel">' +
'<div data-ng-hide="modal.wmdPreview == true" id="wmd-button-bar-' + editorUniqueId + '"></div>' +
'<textarea data-ng-hide="modal.wmdPreview == true" class="wmd-input" id="wmd-input-' + editorUniqueId + '">' +
'</textarea>' +
'</div>' +
'<div data-ng-show="modal.wmdPreview == true" id="wmd-preview-' + editorUniqueId + '" class="pagedownPreview wmd-panel wmd-preview">test div</div>' +
'</div>')(scope);
iElement.html(newElement);
var help = function () {
alert("There is no help");
}
var editor = new Markdown.Editor(converter, "-" + editorUniqueId, {
handler: help
});
var $wmdInput = iElement.find('#wmd-input-' + editorUniqueId);
var init = false;
editor.hooks.chain("onPreviewRefresh", function () {
var val = $wmdInput.val();
if (init && val !== ngModel.$modelValue) {
$timeout(function () {
scope.$apply(function () {
ngModel.$setViewValue(val);
ngModel.$render();
});
});
}
});
ngModel.$formatters.push(function (value) {
init = true;
$wmdInput.val(value);
// editor.refreshPreview();
return value;
});
editor.run();
}
}
});
Can someone explain to me what the following is doing:
scope: {
modal: '=modal'
},
and also the
)(scope);
Here is how I am calling this directive:
<textarea id="modal-data-text"
class="pagedown-admin wmd-preview-46"
data-modal="modal"
data-pagedown-admin
ng-model="home.modal.data.text"
ng-required="true"></textarea>
If anyone can see anything that may not work in 2 then I would much appreciate some help. In particular it seems that the following code returns null:
var $wmdInput = iElement.find('#wmd-input-' + editorUniqueId);
You dropped jQuery, so your code now relies on jQLite. Functions of element objects support less functionality when using jqLite. See the full details in the doc:
https://docs.angularjs.org/api/ng/function/angular.element
var $wmdInput = iElement.find('#wmd-input-' + editorUniqueId);
Under jqLite, the find function only support searching by tag names, ids will not work. You can use the following tricks from ( AngularJS: How to .find using jqLite? )
// find('#id')
angular.element(document.querySelector('#wmd-input-' + editorUniqueId))
$compile is a service that will compile a template and link it to a scope.
https://docs.angularjs.org/api/ng/service/$compile
scope: {
modal: '=modal'
}
allows you to define a isolated scope for the directive with some bindings to the scope in which the directive is declared. '=' is used for two-way data bindings. Other options are '# and &' for strings and functions.
https://docs.angularjs.org/guide/directive

AngularJS click to edit fields such as dropdown

I stumbled upon this article on how to build a click to edit feature for a form. The author states:
What about if you wanted input type="date" or even a select? This
is where you could add some extra attribute names to the directive’s
scope, like fieldType, and then change some elements in the template
based on that value. Or for full customisation, you could even turn
off replace: true and add a compile function that wraps the necessary
click to edit markup around any existing content in the page.
While looking through the code I cannot seem to wrap my head around how I could manipulate the template in such a way that I could make it apply to any angular component, let alone how I can make it apply to a drop down list. Code from article below:
app.directive("clickToEdit", function() {
var editorTemplate = '<div class="click-to-edit">' +
'<div ng-hide="view.editorEnabled">' +
'{{value}} ' +
'<a ng-click="enableEditor()">Edit</a>' +
'</div>' +
'<div ng-show="view.editorEnabled">' +
'<input ng-model="view.editableValue">' +
'Save' +
' or ' +
'<a ng-click="disableEditor()">cancel</a>.' +
'</div>' +
'</div>';
return {
restrict: "A",
replace: true,
template: editorTemplate,
scope: {
value: "=clickToEdit",
},
controller: function($scope) {
$scope.view = {
editableValue: $scope.value,
editorEnabled: false
};
$scope.enableEditor = function() {
$scope.view.editorEnabled = true;
$scope.view.editableValue = $scope.value;
};
$scope.disableEditor = function() {
$scope.view.editorEnabled = false;
};
$scope.save = function() {
$scope.value = $scope.view.editableValue;
$scope.disableEditor();
};
}
};
});
My question is, how can we extend the above code to allow for drop down edits? That is being able to change to the values that get selected.
One approach you might consider is using template: function(tElement,tAttrs ).
This would allow you to return appropriate template based on attributes.
app.directive("clickToEdit", function() {
return {
/* pseudo example*/
template: function(tElement,tAttrs ){
switch( tAttrs.type){
case 'text':
return '<input type="text"/>';
break;
}
},....
This is outlined in the $compile docs

Categories

Resources