I am using an Angular 1.5 Material Design $mdDialog in the recommended way, using controllerAs: "dialog". In the template I have a form: <form name="fooForm". Within the template I can access the form with no problem, e.g. ng-disabled="fooForm.$invalid || fooForm.$submitted".
But how do I access that form from within the $mdDialog controller? From what I read, I would expect to be able to do this:
const doFoo = () => {
if (this.fooForm.$dirty) {
Here this is the dialog controller.
But I get an error: TypeError: Cannot read property '$dirty' of undefined. And sure enough, if I put a breakpoint in the code, the controller has no fooForm property.
I've tried using $scope as well, but when I put a breakpoint in the code $scope has no fooForm property either.
Here's my dialog template:
<md-dialog aria-label="FooBar">
<md-toolbar>
<div class="md-toolbar-tools">
<h2>FooBar</h2>
<span flex></span>
<md-button class="md-icon-button" ng-click="dialog.cancel()">
<md-icon>close</md-icon>
</md-button>
</div>
</md-toolbar>
<form name="fooForm" ng-submit="dialog.ok()" novalidate>
<md-dialog-content>
<div layout="column" layout-padding>
<h2 class="md-headline">Foo</h2>
<div layout="row" layout-xs="column">
...
</div>
</md-dialog-content>
<md-dialog-actions>
<md-button class="md-primary" type="submit" ng-disabled="fooForm.$invalid || fooForm.$submitted">
OK
</md-button>
<md-button ng-click="dialog.cancel()">
Cancel
</md-button>
</md-dialog-actions>
</form>
</md-dialog>
How do I access a form in an $mdDialog from the dialog controller?
you need to assign the form to the Scope of the Controller. Do this by changing the form name from fooForm to dialog.fooForm.
<form name="dialog.fooForm" ng-submit="dialog.ok()" novalidate>
Scope existed and still exist disregarding controllerAs. When you use 'controllerAs xxx' that just mean - put my controller into scope and name it xxx. You can use them together:
function controller($scope) {
var vm = this;
vm.click = function() {}
$scope.click = function() {}
}
<button ng-click="xxx.click()" ...
<button ng-click="click()" ...
Now if you write ng-click="whatever();smth();"angular will use $parse(expression)(scope) to parse this expression.
Now you write form name="formName" -- angular will use $parse("formName").assign(scope, form); and put it to scope.
$parse is quite clever and can easily handle nested properties, to put form to your controller (as xxx): <form name="xxx.myForm"></form>
The form is not defined in your controller as a property and therefore you cannot access the form like if (this.fooForm.$dirty).
However you can easily pass it to your method:
const doFoo = (fooForm) => {
if (fooForm.$dirty) {
...
And in html:
ng-click="dialog.cancel(fooForm)"
Give your controller name when your $mdDialog initialize. see below code:
$scope.showDialog = function (ev) {
$mdDialog.show({
controller: 'myController',
templateUrl: 'template.html',
parent: angular.element(document.body),
targetEvent: ev,
clickOutsideToClose: false,
fullscreen: false // Only for -xs, -sm breakpoints.
})
.then(function (answer) {
$scope.status = 'You said the information was "' + answer + '".';
}, function () {
$scope.status = 'You cancelled the dialog.';
});
};
Make sure you put controller name in quotes when you create separate controller. like controller: 'myController'
if you want to pass a function then not quotes needed like controller: myController,
In html template use ng-submit="ok()" instead of ng-submit="dialog.ok()".
I have created a sample plnkr with your template and it is working fine. check here
EDIT :`
angular.module('BlankApp', ['ngMaterial']).controller('myController', function($scope, $mdDialog) {
$scope.ok = function () {
if ($scope.fooForm.$dirty) {
// do whatever you want
$mdDialog.hide("mesage");
}
};
});`
Related
Say I have several forms each with their own controller that look like this
<div ng-controller="MyController1 as ctrl">
<label>Some label </label>
</div>
<div ng-controller="MyController2 as ctrl">
<label>Some label </label>
</div>
And I have a global controller, that gets the information about the form names. Now I want to find the controllers for each form. For instance, if in my global controller function, I get the name of the first form, how can I find out that its controller is MyController1? Is that even possible?
Calling a controller from another controller is possible. But, I believe the problem you're trying to solve is somewhere else: the architecture of your app.
Ideally, your app should 'react' to changes on the state. The state should be kept in a single place (also called 'single source of truth'), ie. a service. Then you share that service state with as many controllers as you need.
You can either update the service state directly from the controller, or by calling a method on the service itself.
Look at the example below. I hope that sheds some light.
Cheers!
angular.module('app', [])
.service('MyService', function(){
var self = this;
self.state = {
name: 'John'
};
self.changeNameFromService = function() {
self.state.name = 'Peter';
}
})
.controller('Ctrl1', function($scope, MyService){
$scope.state = MyService.state;
$scope.changeName = function(){
// update the state of the scope, which is shared with other controllers by storing the state in a service
$scope.state.name = 'Mary';
}
})
.controller('Ctrl2', function($scope, MyService){
$scope.state = MyService.state;
// call a method defined in service
$scope.changeName = MyService.changeNameFromService;
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="Ctrl1">
Ctrl1: {{state.name}}
<button ng-click="changeName()">Change name!</button>
</div>
<div ng-controller="Ctrl2">
Ctrl2: {{state.name}}
<button ng-click="changeName()">Change name from service!</button>
</div>
</div>
Hi guys I have a very complicated issue.
I have created a reusable directive and now i want to take the user's parameter value and bind it to my form inside the directive.
At the user's side, they are able to call the directive by:
<test-app></test-app>
Then they are able to pass in parameters into the directive :
<test-app user-email="test#hotmail.com"></test-app>
OR
<test-app user-email="{{ userEmail }} "></test-app>
//They can take value from their own controller.
At my Own side at the directive , main.js :
angular.module('TestModule').controller('MainCtrl',['$mdDialog',function($mdDialog) {
this.openDialog = openDialog;
function openDialog(){
$mdDialog.show({
controller: 'DialogCtrl as dialog',
templateUrl: 'dialog.html'
});
}
}]);
angular.module('TestModule').directive('testApp',function(){
return {
controller: 'MainCtrl as main',
scope: {
userEmail :'#'
},
restrict : 'E' ,
templateUrl : 'FormButton.html'
}
});
angular.module('TestModule').controller('DialogCtrl',['$mdDialog',function($mdDialog,$scope){
function submit(){
etc etc . .
}
}
At the FormButton.html:
<md-button ng-click="main.openDialog()"> </md-button>
At the dialog.html:
<md-dialog>
<form>
etc , etc
<input type="email" name="email" placeholder="Email" data-ng-model="userEmail">
etc , etc
</form>
</md-dialog>
Issue: I need to pass in the userEmail value from the user side and bind it to the form so that when the user opens the form , the value is there.
I think because of the templateUrl , the convention way of binding the model doesn't work.
What i have tried:
1) I tried ng-model to bind but the form is in another page so it was not able to read the value.
2) I wanted to pass the value from the directive to controller , I research online but found no viable solution to this problem.
can anyone Kindly Help with this solution?
Take a look at the $mdDialog api docs, especially at the locals option.
locals - {object=}: An object containing key/value pairs. The keys
will be used as names of values to inject into the controller. For
example,
this.userEmail = 'someone#neeae.com';
function openDialog(){
$mdDialog.show({
controller: 'DialogCtrl as dialog',
templateUrl: 'dialog.html',
locals: {
email: this.userEmail // `this` is the main controller (parent).
}
});
}
In the html:
<test-app user-email="{{ main.userEmail }} "></test-app>
DialogCtrl
angular.module('TestModule').controller('DialogCtrl', ['$mdDialog', '$scope', 'email', function($mdDialog, $scope, email) {
// Here you can use the user's email
this.userEmail = email;
function submit() {
//etc etc ..
}
}]);
At the dialog.html:
<md-dialog>
<form>
<!-- etc , etc-->
<input type="email" name="email" placeholder="Email" data-ng-model="dialog.userEmail">
<test-app email="test#hotmail.com"></test-app>
You are passing value in "email" variable. But you are not mapping this variable in directive scope.
Try this once.
<test-app userEmail="test#hotmail.com"></test-app>
I'm trying to implement a bound property for an angular component as explained in the component documentation and this example.
Unfortunately the values I'm assigning at the tag level or in the $onInit methods are never used. Nor is the value printed when I use it as a model value.
You can find the full code on plunker.
My binding definition:
(function(angular) {
'use strict';
function SearchResultController($scope, $element, $attrs) {
var ctrl = this;
ctrl.searchFor = 'nohting-ctor';
ctrl.$onInit = function() {
console.log('SearchResultController.$onInit: searchFor='+ctrl.searchFor);
ctrl.searchFor = 'nothing-int';
};
}
angular.module('myApp').component('searchResult', {
templateUrl: 'searchResult.html',
controller: SearchResultController,
bindings: {
searchFor: '<'
}
});
})(window.angular);
Template:
<p>SearchResult for <span ng-model="$ctrl.searchFor"</span></span></p>
How it's used:
<h1>Main Window</h1>
<search-input on-start-search="$ctrl.startSearch(value)"></search-input>
<search-result search-for="nothing-ext"></search-result>
None of the nothing-* values is evers shown.
Any ideas what's wrong?
The usage of you component is not correct. If you want to pass a string it should be quoted:
<search-result search-for="'nothing-ext'"></search-result>
Then next problem is that this line
<p>SearchResult for <span ng-model="$ctrl.searchFor"</span></span></p>
doesn't make sense, as ngModel directive is only valid for input controls. You want ngBind or simple {{ $ctrl.searchFor }}:
<p>SearchResult for <span ng-bind="$ctrl.searchFor"</span></span></p>
I am trying to pop up a modal to get some input, but the angular binding via ng-model seems to be read only. This is my modal markup:
<script type="text/ng-template" id="signatureWindow.html">
<div class="modal-header">
<h4 class="modal-title" id="myModalLabel">Signature Capture</h4>
</div>
<input type="text" width="100px" ng-model="theName" />
<div class="modal-footer">
<button ng-click="accept()" class="btn btn-primary">Accept</button>
<button ng-click="cancel()" class="btn btn-default">Cancel</button>
</div>
</script>
Then, I invoke this modal as follows:
$scope.getSignatureModal = function(signatureBase64) {
var modalInstance = $modal.open({
templateUrl: 'signatureWindow.html',
controller: 'SignatureModalController',
size: 'lg',
resolve: {
signatureData: function() {
return signatureBase64;
}
}
});
modalInstance.result.then(function(signatureData) {
alert('Signed');
signatureBase64 = signatureData;
}, function() {
alert('Canceled');
});
};
And the following controller code for the modal:
MlamAngularApp.controller('SignatureModalController', function($scope, $modalInstance, signatureData) {
$scope.base64 = signatureData;
$scope.thename = "NamesRus";
$scope.accept = function() {
debugger;
$modalInstance.close($scope.thename);
}
$scope.cancel = function() {
$modalInstance.dismiss('cancel');
}
});
The modal pops up as expected, and the input has the initial value "NamesRus", but when I close the modal, invoking accept in the modal controller, $scope.thename still has it's initial value, not any new value I type when the modal is active. What could be wrong here?
NOTE: At the debugger breakpoint in accept, no matter what I type in the modal's input, theName still has the initial assigned value.
MORE: My Plunker for this works fine. It's when in-place, in an ASP.NET MVC5 project, that I get the strange behaviour.
I think that you mix up two differents scopes.
If you want several variables to be passed to and retrieved from the modal you have to mention them:
in the resolve attribute
resolve: {modalData: function (){return {signatureData:$scope.base64,name:$scope.theName}}}
pass modalData as dependencie to your controller
MlamAngularApp.controller('SignatureModalController', function($scope, $modalInstance, modalData)
update the modal controller $scope with modalData
$scope.signatureData = modalData.signatureData;
$scope.name=modalData.name;
invoke
$modalInstance.close({signatureData:$scope.base64,name:$scope.theName})
reassign the original $scope with the result of promise
modalInstance.result.then(function (data) {
$scope.base64 = data.signatureData;
$scope.thename=data.name;
}
take a look at this plunker forked from ui-boostrap modal orginal example: http://plnkr.co/edit/whLSYt?p=info
I have angular working in one of my ASP.NET MVC applications. I am using two html templates with Angular Routing. One is a list of current Favorites that comes from the database and is serialized into json from my Web API and used by angular to list those items from the database.
The second html template is a form that will be used to add new favorites. When the overall page that includes my angular code loads, it has a cookie named currentSearch which is holding the value of whatever the last search parameters executed by the user.
I would like to inject this value into my angular html template (newFavoriteView.html) for the value of a hidden input named and id'd searchString.
I have tried using jQuery, but had problems, plus I would much rather do this inside of angular and somehow pass the value along to my template or do the work inside the view(template). However, I know the latter would be bad form. Below is the code I think is important for one to see in order to understand what I am doing.
Index.cshtml (My ASP.NET VIEW)
#{
ViewBag.Title = "Render Search";
ViewBag.InitModule = "renderIndex";
}
<div class="medium-12 column">
<div data-ng-view=""></div>
</div>
#section ngScripts {
<script src="~/ng-modules/render-index.js"></script>
}
Setting the cookie in the MVC Controller
private void LastSearch()
{
string lastSearch = null;
if (Request.Url != null)
{
var currentSearch = Request.Url.LocalPath + "?" +
Request.QueryString;
if (Request.Cookies["currentSearch"] != null)
{
lastSearch = Request.Cookies["currentSearch"].Value;
ViewBag.LastSearch = lastSearch;
}
if (lastSearch != currentSearch)
{
var current = new HttpCookie("currentSearch", currentSearch){
Expires = DateTime.Now.AddDays(1) };
Response.Cookies.Set(current);
var previous = new HttpCookie("lastSearch", lastSearch) {
Expires = DateTime.Now.AddDays(1) };
Response.Cookies.Set(previous);
}
}
}
render-index.js
angular
.module("renderIndex", ["ngRoute"])
.config(config)
.controller("favoritesController", favoritesController)
.controller("newFavoriteController", newFavoriteController);
function config($routeProvider) {
$routeProvider
.when("/", {
templateUrl: "/ng-templates/favoritesView.html",
controller: "favoritesController",
controllerAs: "vm"
})
.when("/newsearch", {
templateUrl: "/ng-templates/newFavoriteView.html",
controller: "newFavoriteController",
controllerAs: "vm"
})
.otherwise({ redirectTo: "/" });
};
function favoritesController($http) {
var vm = this;
vm.searches = [];
vm.isBusy = true;
$http.get("/api/favorites")
.success(function (result) {
vm.searches = result;
})
.error(function () {
alert('error/failed');
})
.then(function () {
vm.isBusy = false;
});
};
function newFavoriteController($http, $window) {
var vm = this;
vm.newFavorite = {};
vm.save = function () {
$http.post("/api/favorites", vm.newFavorite)
.success(function (result) {
var newFavorite = result.data;
//TODO: merge with existing topics
alert("Thanks for your post");
})
.error(function () {
alert("Your broken, go fix yourself!");
})
.then(function () {
$window.location = "#/";
});
};
};
favoritesView.html
<div class="container">
<h3>New Favorite</h3>
<form name="newFavoriteForm" ng-submit="vm.save()">
<fieldset>
<div class="row">
<div class="medium-12 column">
<input name="searchString" id="searchString" type="hidden"
ng-model="vm.newFavorite.searchString"/>
<label for="title">Name</label><br />
<input name="title" type="text"
ng-model="vm.newFavorite.name"/>
<label for="title">Description</label><br />
<textarea name="body" rows="5" cols="30"
ng-model="vm.newTopic.description"></textarea>
</div>
<div class="medium-12 column">
<input type="submit" class="tiny button radius" value="Save"/> |
Cancel
</div>
</div>
</fieldset>
</form>
</div>
My current attepts have been using jQuery at the end of the page after Angular has loaded and grab the cookie and stuff it in the hidden value. But I was not able to get that to work. I also thought about setting the value as a javascript variable (in my c# page) and then using that variable in angular some how. AM I going about this the right way?
Or should it be handled in the angular controller?...
I'm new to angular and the Angular Scope and a bit of ignorance are getting in the way. If any other info is needed I can make it available, thanks if you can help or guide me in the right direction.
You can do it by reading the cookie value using JavaScript, set it as a property of the $scope object and access it on the template.
//Inside your controllers
function favoritesController($http, $scope) {
//Get the cookie value using Js
var cookie = document.cookie; //the value is returned as a semi-colon separated key-value string, so split the string and get the important value
//Say the cookie string returned is 'currentSearch=AngularJS'
//Split the string and extract the cookie value
cookie = cookie.split("="); //I am assuming there's only one cookie set
//make the cookie available on $scope, can be accessed in templates now
$scope.searchString = cookie[1];
}
EXTRA NOTE
In AngularJS, the scope is the glue between your application's controllers and your view. The controller and the view share this scope object. The scope is like the model of your application. Since both the controller and the view share the same scope object, it can be used to communicate between the two. The scope can contain the data and the functions that will run in the view. Take note that every controller has its own scope. The $scope object must be injected into the controller if you want to access it.
For example:
//inject $http and $scope so you can use them in the controller
function favoritesController($http, $scope) {
Whatever is stored on the scope can be accessed on the view and the value of a scope property can also be set from the view. The scope object is important for Angular's two-way data binding.
Sorry if I'm misunderstanding or over-simplifying, but...assuming JavaScript can read this cookie-value, you could just have your controller read it and assign it to a $scope variable?
If JavaScript can't read the value, then you could have your ASP write the value to a JavaScript inline script tag. This feels yuckier though.
Update to show controller-as example.
Assuming your HTML looked something vaguely like this:
<div ng-controller="MyController as controller">
<!-- other HTML goes here -->
<input name="searchString" id="searchString" type="hidden" ng-model="controller.data.currentSearch"/>
Then your controller may look something like this:
app.controller('MyController', function ($scope, $cookies) {
$scope.data = {
currentSearch: $cookies.currentSearch
};
// Note that the model is nested in a 'data' object to ensure that
// any ngIf (or similar) directives in your HTML pass by reference
// instead of value (so 2-way binding works).
});