Web project: backend - ASP.NET, frontend - AngularJS.
I have the "main page" with mainApp module.
Also i have the another page with the registration, which is the responsibility of LoginLayerApp.
What i want: when you click on "main window" i need pop-up window with the registration content (html, css, angularController and so on).
What was tried:
Loaded using JQuery, implement the DOM and call angular.bootstrap by
hands.
Loaded via $http and using ngDialog library to open a pop-up
window.
Use the iframe.
The problem is that the loaded naked HTML, but I do not know how to inject LoginLayerApp, to then rebuild the naked HTML. I can do it only once, but not as usual, when angular always rebuild view by changes in controller (for example, different ng-class depending on the width of the screen).
How to do the most right? Perhaps I should do differently to organize something?
RequireJS, WebPack will not work, too much legacy-project implementation for such a trifle.
Example of registration page:
#using Inventum.ViewModels.Account
#using Microsoft.Owin.Security
#{
Layout = null;
}
<link href="~/Content/css/layers/Login/loginRegistrationLayer.min.css" rel="stylesheet" />
<div ng-app="loginLayerApp"
ng-controller="loginLayerController as loginCtrl"
ng-init="init(); returnUrl = '#ViewBag.ReturnUrl'; type = ('#ViewBag.Type' == '') ? 'login' : '#ViewBag.Type'; form={}; user={};"
esc-key="closeLayer()"
class="login-app-container"
tabindex="0">
<div class="container" ng-class="{'mobile': !desktop, 'desktop': desktop }" ng-switch on="type">
<!-- =============== LOGIN =============== -->
<div class="registration-container" ng-switch-when="login">
{{loginCtrl.desktop}}
{{desktop}}
<h1>#Html.ClickZone("NewRegLogin.Login.Title")</h1>
<div class="main-label">
<span>#Html.ClickZone("NewRegLogin.Login.HelpLabel")</span>
<br ng-show="phoneBreakpoint">
</div>
<!-- ======== Errors block ======== -->
<!-- ======== Errors block ======== -->
<div class="options">
<ng-form name="form.userForm" ng-class="{'showMe': nosocial && !desktop}" novalidate enter-key="trySubmit = true; submitForm(form.userForm.$valid, user)">
<!-- <form name="userForm" ng-submit="submitForm(userForm.$valid)" novalidate> -->
<div class="emailFocusHandler" ng-hide="desktop || nosocial" ng-click="nosocial = true;"></div>
<div class="group">
</div>
<div ng-show="desktop || nosocial">
<div class="group">
</div>
<div class="help-buttons">
</div>
<div class="error-block" ng-if="loginFailed && answerCode == ANSWERS.LOGIN.UserNoutFound">
</div>
<button type="submit" ng-click="trySubmit = true; submitForm(form.userForm.$valid, user)">#Html.ClickZone("NewRegLogin.SignIn") </button>
</div>
</ng-form>
<div class="delimeter" ng-show="desktop || nosocial"></div>
</div>
</div>
<!-- =============== LOGIN =============== -->
</div>
#section scripts {
<!-- ANGULARJS -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular-route.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular-messages.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular-animate.min.js"></script>
#Scripts.Render("~/bundles/js/layer/newLogin")
<script src="https://www.google.com/recaptcha/api.js?onload=vcRecaptchaApiLoaded&render=explicit" async defer></script>
}
What I would do is create the modal window (pop-up window) as a angular-ui modal. (their modal docs). That way you don't have to write your own modal code.
When you implement it, the modal will get its own controller and its own html file and the implementation will look like this:
Current Controller-
appName.controller('loginLayerController', ["$scope","uibModal", function ($scope, $uibModal){
$scope.openComponentModal = function () {
var modalInstance = $uibModal.open({
templateUrl: 'myModalContent.html',
controller: 'myModalController',
controllerAs: 'myModalController'
}
});
}])
And from there you can put your form into myModalContent.html, and hide and show buttons there accordingly
I'm not sure, but you can try to create component, that will show your errors. By default set in invisible. In controllers you can manage how many and when you should show your error messages.
Related
I'm listing my schedule entries, now I'm trying to display a modal when clicking on edit for a specific entry. My goal is to be able to edit the values of my entries. But first, can't get the modal to even display
See my plunker or code below
html
<div class="container" ng-app="appUserSchedule">
<div ng-controller="CtrlUserSchedule" >
<div class="row">
<div class="col-md-10 col-md-offset-1">
<div class="panel panel-default">
<div class="panel-heading">User Schedule</div>
<div class="panel-body">
<div ng-cloak style="max-width:400px;">
<header>
<h3>User schedule</h3>
</header>
<ul>
<li ng-repeat="x in userscheds">{{x.week_day}} {{x.time_start}}-{{x.time_end}}
<span ng-click="showModal($index)" style="cursor:pointer;">Edit</span>
<span ng-click="removeItem($index)" style="cursor:pointer;">Delete</span>
</li>
</ul>
<div>
<div>
<div>
<input placeholder="Add user schedule entry here" ng-model="addMe">
</div>
<div>
<button ng-click="addItem()">Add</button>
</div>
</div>
<p>{{errortext}}</p>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Modal -->
<div class="modal fade" id="modalContent.html" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title">Edit</h4>
</div>
<div class="modal-body">
<timepicker ng-model="dt1" hour-step="1" minute-step="15" show-meridian="true"></timepicker>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</div> <!-- ng-controller -->
</div> <!-- ng-app -->
js
var app = angular.module("appUserSchedule", []);
app.controller("CtrlUserSchedule", function($scope,$http,$location) {
$http.get('userschedule.json').success(function(data, status, headers, config) {
$scope.userscheds = data.userschedules;
console.log(data);
}).error(function(data, status, headers, config) {
console.log("No data found..");
});
$scope.addItem = function () {
$scope.errortext = "";
if (!$scope.addMe) {return;}
if ($scope.userscheds.indexOf($scope.addMe) == -1) {
$scope.userscheds.push($scope.addMe);
} else {
$scope.errortext = "The item is already in your list.";
}
}
$scope.removeItem = function (x) {
$scope.errortext = "";
$scope.userscheds.splice(x, 1);
}
$scope.showModal = function (action, x) {
var modalInstance;
modalInstance = $modal.open({
templateUrl: 'modalContent.html',
controller: 'CtrlUserSchedule',
scope: $scope
});
// This code for later
// Save User Schedule Entry after making a change, then close modal
saveUserScheduleEntry = function(event) {
$http.put('').success(function(eventsuccess){
}).error(function(err){
/* do something with errors */
});
modalInstance.close();
};
// This code for later
// Close modal
closeUserScheduleEntry = function(event) {
$http.put('').success(function(eventsuccess){
}).error(function(err){
/* do something with errors */
});
modalInstance.close();
};
}
});
ng-repeat creates its own scope, hence the scope of your controller and the scope inside of the ng-repeat is not the same.
therefore $scope.showModal will not be defined inside of the ng-repeat.
the main mistake you are doing here is the "there always has to be a dot in there" rule.
look here:
Why don't the AngularJS docs use a dot in the model directive?
i would recommend to take a little tutorial about the most made angularjs mistakes first, because indeed there is some stuff to know about angularjs before you stop running into little traps like this.
furthermore you did not inject $modal to your controller which should lead in an error like $modal is undefined or something like that.
also you didn't even create/add the corresponding html file you are about to open.
and last but not least you didn't add any dependencies to your angular module regarding bootstrap. so your app won't be able to use/inject $modal anyhow.
see a working plunkr here:
http://plnkr.co/edit/rOjXt1C4E6lHRXUqHdHq?p=preview
have a look at the line where i am "putting the dot in"
$scope.ctrl = this;
i replaced the templateUrl by a simple template because i think it's enough to give the idea.
in the end i have to say that there was so many things wrong about your code that you really should try to debug more on your own and try to accomplish things more step by step.
since there have been like 4-5 mistakes in it this code barely can be your own, but rather is some random copy&paste stuff from anywhere.
otherwise you could not have accomplished that many different lines of code which don't do anything, sorry.
First is you need to add ui.bootstrap module to you ar angular app and pass $modal to your controller like this
var app = angular.module("appUserSchedule", ['ui.bootstrap']);
app.controller("CtrlUserSchedule", function($scope,$http,$location, $modal) {
Then you need to add modalContent.html. This displays the html inside modal window
This may not be the answer, but make sure showModal is receiving the correct arguments. Appears you are only sending $index.
Would have simply made this a comment, but not quite there on the reputation yet. :-)
I'm having trouble figuring out how to target elements in our single-page application.
I'm testing mostly authenticated pages, so the data is being mocked for protractor using: ngMockE2E and $httpBackend
Let's say there are 4 pages/views: Home | About | Settings | Sign out
The Settings page structure is roughly:
<div ng-view class="ng-scope">
<div class="row ng-scope" ng-controller="SettingsCtrl as ctrl">
<h1>Settings Main Page</h1>
<div ng-if="show.more">
More Info
[...]
</div>
</div>
</div>
If you click the more info link, the view will change to that page where the only major difference is in the ng-controller.
<div ng-view class="ng-scope">
<div class="row ng-scope" ng-controller="SettingsCtrl">
<h2>Settings More Info Page</h2>
[...]
</div>
</div>
In protractor I cannot figure out how to access the elements within the more info page - but I can locate the elements on the main settings page just fine.
For example:
var main_title = element(by.tagName('h1')).isPresent();
expect(main_title).toBe(true); // Will return true
var moreInfo_title = element(by.tagName('h2')).isPresent();
expect(moreInfo_title).toBe(true); // Will return false
Why can't I access elements within a view?
You might need to wait for the presence of the h2 element:
// click "More info"
var EC = protractor.ExpectedConditions;
var moreInfo_title = element(by.tagName('h2'));
browser.wait(EC.presenceOf(moreInfo_title), 5000);
expect(moreInfo_title.isPresent()).toBe(true);
You should try clicking the link then check to see if the more info title exists.
var main_title = element(by.tagName('h1')).isPresent();
expect(main_title).toBe(true);
var link = element(by.linkText('More Info'));
link.click().then(function() {
var moreInfo_title = element(by.tagName('h2')).isPresent();
expect(moreInfo_title).toBe(true);
});
I am new to Angualr Js, I am trying to implement a simple app, Below is my requirement,
My application has three roles, basing on the roles i am rendering different pages.
In the role based rendered page, i have few links, on click of each link, i am rendering few more features.
Initially i am using ng-include to render my view by calling a method in my controller which hits the service and get the role and basing on the role, i am returning the view.
Home.html
<html>
<head>
<meta charset="ISO-8859-1">
<title></title>
</head>
<body>
<div class="container" data-ng-app="vendorPortalApp" data-ng-controller="vendorPortalController">
<div data-ng-include="getView()">
</div>
</div>
</body>
I am intializing the angular services and controller along with routes here
app.js
angular.module('vendorPortalApp', ['vendorPortalApp.controllers','vendorPortalApp.services','ngRoute']).config(['$routeProvider', function($routeProvider) {
$routeProvider.
when("/home", {controller: "vendorPortalController"}).
when("/home/resetPassword", {templateUrl: "features/resetPassword.html", controller: "vendorResetPasswordController"}).
when("/home/editProfile",{templateUrl: "features/editProfile.html",controller:"vendorEditProfileController"}).
otherwise({redirectTo: '/home'});}]);
Controller.js
angular.module('vendorPortalApp.controllers', []).controller('vendorPortalController', function($scope,serviceApi) {
$scope.role = null;
serviceApi.getUserSessionData().success(function (response) {
$scope.role = response;
});
$scope.getView = function() {
if($scope.role == "SuperAdmin"){
return "views/SuperAdmin.html";
}else if($scope.role == "Admin"){
return "views/Admin.html";
}else if($scope.role == "User"){
return "views/User.html";
}else {
return null;
}
}}).controller('vendorResetPasswordController',function($scope,serviceApi){
}).controller('vendorEditProfileController',function($scope,serviceApi){
});
views/SuperAdmin.html
<div class="left-content">
Edit Profile
Reset Password
</div>
<div class="right-content">
<ng-view></ng-view>
</div>
I have a folder named features, in which i have editProfile.html, and resetPassword.html partial views, i wanted to get these partial views rendered on anchor click on SuperAdmin.html
editProfile.html
<section data-ng-controller="vendorEditProfileController">
<h1>Edit Profile</h1>
Firstname : <input type="text" name="firstname"/>
Lastname : <input type="text" name="lastname"/>
<input type="submit" value="Submit" />
</section>
ResetPassword.html
<section data-ng-controller="vendorResetPasswordController">
<h1> Reset Password </h1>
Password : <input type="password" name="reset-password">
Confirm Password : <input type="password" name="confirm password">
<input type="submit" value="Submit"/>
</section>
I am able to render the views based on role, Once the SuperAdmin.html view got rendered, i have few anchor links on the page, on each anchor link it references to #/home/editProfile and #/home/resetPassword, these url pattern match in the route configuration and render the appropriate feature from the features folder.
The only issue i am facing is, Lets say i have clicked on reset password link, my url gets updated to http://locahost/vendorapp/home.html#/home/resetPassword
and i get the reset password feature rendered over my page, Now when i am reloading the page, it gives me an issue, it wipes away the resetPassword.html feature, and even when we click on reset password link, it does not generate the view. Any help on this will be appreciated!
I know it might be a simple question, but I'm frustrated here, and I can't make it work. I'm new to AngularJS, and I'm trying to implement a modal dialog (or find one) with these conditions:
Dialog content might come from anywhere—a string template, a script template, or a template from a URL
Dialog title and actions will come from the caller, not the callee. In other words, the parent scope decides the title and which action buttons should exist in the modal dialog (many dialogs I found encapsulate the title and action buttons in the template itself, for example this one)
Content of the template should be totally independent from parent scope (caller). In fact, it might not even be written in AngularJS. It might use jQuery.
In case the loaded template is in AngularJS, it should encapsulate its controller. For example, ng-include doesn't like <script> tags.
There is a workaround for it (here, here and here) but the idea of decorating a script tag with text/javascript-lazy is very smelly and dirty, let alone that I want the content HTML to be self-contained and executable in case it's not loaded as the content of an AngularJS modal dialog.
Communication between the parent scope and the content should be done via a common contract (JavaScript events come to my mind)
I've tried ngDialog, but the problem is that the container should pass the controller to the loaded template. That's not what I want.
In Bootstrap dialog also it seems that you have to pass the controller from the parent scope to the dialog content. This breaks the very notion of encapsulation. It's not desirable. Also, it's dependent on dialog result, which is not desirable either.
I recommend use Angular-UI library. You can easy create any dialog a-la "Twitter Bootstrap":
Include js in your page head.
<script src="/desktop/libs/angular-bootstrap/ui-bootstrap.js"></script>
<script src="/desktop/libs/angular-bootstrap/ui-bootstrap-tpls.js}"></script>
Include modules at app initialization.
var Application = A.module('MyApp', [ 'ui.bootstrap', 'ui.bootstrap.modal' ]);
Inject in jour controller $modal:
(function (A){
"use strict";
A.module("MyApp").controller("OpenDlg", [ "$scope", "$modal", function($scope, $modal){
$scope.openDlg = function(){
$modal.open({
controller : 'CategoryAddController',
templateUrl : '/admindesktop/templates/category/add/'
}).result.then(function(modalResult){
console.log(modalResult);
});
};
} ]);
}(this.angular));
For example, simple template for dialog:
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title text-center">Создание новой категории</h4>
</div>
<form class="modal-body form-horizontal" name="categoryForm">
<div class="form-group">
<label for="name" class="control-label col-xs-3">Название</label>
<div class="col-xs-9">
<input name='name' type="text" class="form-control" ng-model="category.name" maxlength=50 required ng-required="true"/>
</div>
<div class="row has-error" ng-show="errors.name">
<p ng-repeat="error in errors.name">{{ error }}</p>
</div>
</div>
<div class="container-fluid" ng-show="errors.length > 0">
<div class="row">
<p class="text-center text-danger" ng-repeat="error in errors">{{ error }}</p>
</div>
</div>
</form>
<div class="modal-footer">
<button class="btn btn-primary" ng-click="save()" ng-disabled="categoryForm.$invalid">Сохранить</button>
<button class="btn btn-default" ng-click="cancel()">Отмена</button>
</div>
</div>
Main: controller for modal window:
(function(A) {
"use strict";
var app = A.module('MyApp');
app.controller('CategoryAddController', [ '$scope', '$modalInstance', 'Category', 'growl', function($scope, $modalInstance, Category, growl) {
$scope.save = function() {
var category = new Category($scope.category);
category.$save(function() {
growl.success('Категория успешно создана');
$modalInstance.close(true);
}, function(response) {
$scope.errors = response.data;
});
};
$scope.cancel = function() {
$modalInstance.close(false);
};
} ]);
}(this.angular));
I use Service for data changing between modal controller and parent scope:
(function(A){
"use strict";
A.module("MyApp").service('Storage', function(){
return {
storedData: undefined
};
});
}(this.angular));
In parent scope:
Storage.storedData = ...; //For example, selected row of table
In modal controller:
$scope.item = Storage.storedData; //Selected row of table
Also angular have special module type, value.
I've looked at quite a few links for people trying to add content / change the template for a Dojo Dialog. This was the most promising.
However, whenever I do something like this:
Dialog declared in HTML:
<div class="djDialog" id="dgViewer" data-dojo-type="TemplatedDialog" data-dojo-props="title: 'My Dialog', draggable:false"></div>
Dialog Template:
<div class="dijitDialog" role="dialog" aria-labelledby="${id}_title">
<div data-dojo-attach-point="titleBar" class="dijitDialogTitleBar">
<span data-dojo-attach-point="titleNode" class="dijitDialogTitle" id="${id}_title"></span>
<span data-dojo-attach-point="closeButtonNode" class="dijitDialogCloseIcon" data-dojo-attach-event="ondijitclick: onCancel" title="${buttonCancel}" role="button" tabIndex="-1">
<span data-dojo-attach-point="closeText" class="closeText" title="${buttonCancel}">x</span>
</span>
</div>
<!-- containerNode from original Dialog template -->
<div data-dojo-attach-point="containerNode" class="dijitDialogPaneContent">
<!-- All "custom" content -->
<div data-dojo-type="dijit.layout.BorderContainer" data-dojo-props="design:'headline'" style="height:300px">
<div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'center'">
</div>
<div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'top'">
<button data-dojo-type="dijit.form.Button">Edit</button>
<button data-dojo-type="dijit.form.Button">Status</button>
</div>
</div>
<!-- End "custom" content -->
</div>
</div>
Custom Dialog class:
define([
'dojo/_base/declare',
'dijit/Dialog',
'dijit/_TemplatedMixin',
'dojo/text!./dialog_templates/View.html'],
function(
declare,
Dialog,
_Mixin,
_template){
return declare('TemplatedDialog', [Dialog, _Mixin], {
templateString : _template,
constructor : function(){
}
})
})
In my console (using Chrome) I just see:
<div data-dojo-attach-point="containerNode" class="dijitDialogPaneContent" style="width: auto; height: auto; "></div>
... and empty node where content should be.
So far I haven't found anyone who seems to have successfully extended dijit.Dialog in terms of templates. Is this possible?
Edit
After trying some variations on this template:
<div class="dijitDialog" role="dialog" aria-labelledby="${id}_title">
<div data-dojo-attach-point="titleBar" class="dijitDialogTitleBar">
<span data-dojo-attach-point="titleNode" class="dijitDialogTitle" id="${id}_title"></span>
<span data-dojo-attach-point="closeButtonNode" class="dijitDialogCloseIcon" data-dojo-attach-event="ondijitclick: onCancel" title="${buttonCancel}" role="button" tabIndex="-1">
<span data-dojo-attach-point="closeText" class="closeText" title="${buttonCancel}">x</span>
</span>
</div>
<!-- containerNode from original Dialog template -->
<div class="dijitDialogPaneContent">
<div data-dojo-type="dijit.layout.BorderContainer" data-dojo-props="design:'headline'" style="height:100%;width:100%">
<div data-dojo-attach-point="containerNode" data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'center'"></div>
</div>
<div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'bottom'">
<button data-dojo-type="dijit.form.Button">Edit</button>
</div>
</div>
</div>
The error Uncaught TypeError: Cannot read property '0' of undefined is being thrown. This is the stack, if it helps. I'm using the uncompressed version from the Google CDN to help in debugging.
_childElements dojo.js.uncompressed.js:8341
getStepQueryFunc dojo.js.uncompressed.js:8597
query dojo.js.uncompressed.js:9005
query dojo.js.uncompressed.js:5248
_2._checkIfSingleChild _ContentPaneResizeMixin.js:2
_4._size Dialog.js:2
_4.show Dialog.js:2
Removing the data-dojo-type and -props from the containerNode resolves this, but it's still not getting things closer to having a working custom templated Dialog.
Reason for troubles with doing templating on a contentpane is, that whatever template-contents you put into the domNode referenced with the attach-point 'containerNode', you will loose on startup.
If there is no 'href' nor 'content' attributes set, they will simply be set to an empty string, thus leaving the Dialog.containerNode.innerHTML == ""
You have no need for deriving from _TemplatedMixin as the Dialog itself is a templated widget. Instead change this to _WidgetsInTemplateMixin to compensate for the BorderContainer layout widgets and your dijit.form contents. Also, your markup within the custom template should be pre-required, so you could go with something like this here:
Change the template from old attachpoint for container to this
<div data-dojo-attach-point="containerNode"
data-dojo-type="dijit.layout.ContentPane"
data-dojo-props="region:'center'">
Then add requirements to your markup widgets in the template plus the _WidgetsInTemplateMixin:
define(["dijit/Dialog",
"dijit/_WidgetsInTemplateMixin",
"dojo/text!./dialog_templates/View.html",
// rest are for rendering example
"dijit/layout/BorderContainer",
"dijit/layout/ContentPane",
"dijit/form/Button"
}. ... );
Result should ressemble this, keeping your template View.html change in mind:
define([
'dojo/_base/declare',
'dijit/Dialog',
"dijit/_WidgetsInTemplateMixin",
"dojo/text!./dialog_templates/View.html",
// rest are for rendering example
"dijit/layout/BorderContainer",
"dijit/layout/ContentPane",
"dijit/form/Button"],
function(
declare,
Dialog,
_Mixin,
_template){
return declare('TemplatedDialog', [Dialog, _Mixin /*careful, widgetsintemplate is tricky*/ ], {
templateString : _template
})
})
You can fiddle here
EDIT:
As there is troubles with dialogs containing borderlayouts (its not unheard of anyways) here's a workaround:
_checkIfSingleChild: function() {
delete this._singleChild;
domClass.toggle(this.containerNode, this.baseClass + "SingleChild", !!this._singleChild);
},
templateString: '....'
Im not certain of the consequences, im thinking the borderlayout of yours might start to misbehave if you try to programmatically change its contents and dimensions.. But it will render - at least it does here: updated fiddle