Can an Angular controller bind to multiple parts of a web page? - javascript

Suppose that I've written a web page that requires the user to key in data into forms. Suppose that there are two parts of the page that are completely separate in the DOM, yet are logically related. I need to bind them to a single model.
The first (bad) solution I can think of is to completely restructure the page so that everything to be bound to the model is contained in a single <div> element (or perhaps even put the controller on the <body> element).
Another solution might be to bind the inputs to a object that could be made a member of two different controllers, but that sounds like a bit of a kludge.
Suppose the HTML looks like this:
<input ng-model='data1' />
<div>
<!-- something complicated -->
</div>
<input ng-model='data2' />

You can create a shared service and inject into both controllers. This can be achieved by either the factory or service pattern. See SO post angular.service vs angular.factory for some insight on the difference/similarity between the two.
This is the Angular way to do this. See the AngularJS services docs for more information. A simple example may include...
<div ng-app="app">
<div ng-controller="ctrlA"></div>
<div ng-controller="ctrlB"></div>
</div>
app.service('sharedService', [function () {
this.someValue = 'yo!'
}]);
app.controller('ctrlB', ['$scope', 'sharedService', function($scope, sharedService) {
console.log(sharedService.someValue) // yo!
}]);
app.controller('ctrlA', ['$scope', 'sharedService', function($scope, sharedService) {
console.log(sharedService.someValue) // yo!
}]);
JSFiddle Link

Related

How to append dynamic html with dynamic data in angular1?

Basically I want to achieve like this scenario:
https://i.stack.imgur.com/LLTcK.png
My research led me to $compile, $trustAsHtml,at last directive.
In $compile and $trustAsHtml I can only append static template or only html but can't use dynamic things such ui-sref, ng-click etc.
So, I tried to create directive it is not working and also I am unable to add multiple template on click.
controller :
app.controller('Ctrl', ['$rootScope', '$scope',function ($rootScope, $scope)
{
$rootScope.enableDirective=false;
if(userHasOneApp){// checking some at least one app then only do action
$rootScope.appicon="img_url"; // data which i am passing
$rootScope.appname="App_name"; // data which i am passing
$rootScope.enableDirective=true;
}
}]);
custom directive:
app.directive('headerTemplate', function () {
return {
template:'<a ui-sref="/event" ng-click="editIt()">'
+'<img src="{{appicon}}"></a>'
+'<span>{{appname}}</span>',
scope:{
appname:'=',
appicon:'='
}
};
});
Header view :
<div> class="headerdiv">
<ul ng-if="enableDirective">
<li header-template appicon="appicon">
</li>
</ul>
</div>
Main view :
<div> class="maindiv">
<ui-view></ui-view> <!--basically I want to append template here -->
<button>Add next template</button>
</div>
Where I am doing wrong ?
Well i was facing the same issue
Check out the following link
This will surely help you.
I have implemented this and it worked in my scenario where i wanted to serve a directive when required i.e Lazy Loading of directive
https://www.codeproject.com/Articles/838402/Lazy-loading-directives-in-AngularJS-the-easy-way

AngularJS: How to Update ng-view using $route.reload()

I have searched through a lot of questions regarding $route.reload() and I think I still have a fundamental misunderstanding of how it and Angular are actually working. I really want to understand what is actually going on (not just a quick fix) so I apologize if this question gets rather verbose.
I am trying to create an app that will allow the user to input information into a form and then use ng-view and a controller LivePreviewController to display that form data in a "live preview" of the form which has some unique styling and other information associated with the form that the user does not input themselves.
This is very simple to do if the "live preview" is occurring on the same page as the user is inputting information using something like this
<input type="text" ng-model="form_name" />
<div ng-bind="form_name"></div>
What I would like to do is something like this.
<!--Location is: localhost:8888/form_page.html -->
<input type="text" ng-model="form_name" />
<!-- ng view is displaying the partial view: styled_form.html -->
<div ng-view></div>
Then I have another page styled_form.html which is the partial view that gets displayed by the ng-view div.
<!-- partial view page is styled_form.html -->
<div ng-bind="form_name"></div>
Both of these pages are controlled by the same controller LivePreviewController.
There are a lot of things I imagine that I am probably doing incorrectly. First thing could be the routing. I had some issues with the routing as app is only used on one page of a multi-page website my routes are setup as follows.
var previewApp = angular.module('previewApp', ['ngRoute']);
previewApp.config(['$routeProvider', function($routeProvider){
$routeProvider
.otherwise({
redirectTo:'/styled_view.html',
controller: 'LivePreviewController'
});
}]);
//Controller Code
previewApp.controller('LivePreviewController', [$scope, $route, function($scope, $route){
$scope.form_name = 'temporary name';
}]);
All of this works as expected when the form_page.html is originally loaded, but when the user changes the name in the text input the ng-view displaying my styled_form.html page is not changed. This also makes sense because the styled_form.html needs to be reloaded with the new information.
Here is where the noob starts. My first inclination was to create a refresh button and add a function in my LivePreviewController to refresh the page using $route.reload()
previewApp.controller('LivePreviewController', [$scope, $route, function($scope, $route){
$scope.form_name = 'temporary name';
$scope.refreshPage = function() {
$route.reload();
console.log($scope.form_name) //to ensure scope is updating
};
}]);
And then updated my html to include the button as follows
<!--Location is: localhost:8888/form_page.html -->
<input type="text" ng-model="form_name" />
<button ng-click="refreshPage()"> Update the Page </button>
<!-- ng view is displaying the partial view: styled_form.html -->
<div ng-view></div>
The function is working just fine when the button is clicked and the updated scope variable is displaying in the console, but my live preview is not updating. After looking through the Angular documentation I have a hunch as to what is happening. It states...
Causes $route service to reload the current route even if $location hasn't changed.
As a result of that, ngView creates new scope and re-instantiates the controller.
So if I understand the docs correctly, which I obviously don't, what is actually happening on $route.reload() is that when styled_form.html is getting reloaded the updated $scope.form_name is getting wiped out and the original $scope value is getting shown on the page. So...
Question 1: am I understanding this correctly i.e. is $route.reload() wiping out my $scope and reloading the old $scope variable, or is my $route.reload() just not reloading the page at all?
Now if I am interpreting my issue correctly my guess (seen suggested on here in other topics) is that the solution would be to create some type of service to store my data and then call that service inside of the controller. This workflow would look like
User input --> Data stored in some Service(factory) --> Data Passed to $scope --> >$scope value displayed in partial view (styled_form.html)
But I am lost as to how to dynamically store the user input inside of a service. Ideally this would all occur in realtime (as with the simple first example) as I would like to avoid having a refresh button. Could this be accomplished by creating a function in my controller that updates the values inside my service every few seconds? I need someone with better ideas than myself to point me in the right direction.
Question 2: Is this the correct workflow assuming Q1 is correct and if so what is the correct way to set up my service to handle the user input and pass it to another html page dynamically?
All help much appreciated
Having a service to store data and same controller to serve both view is fine.
Data saving could be done each time, when controller variables are changing.
When new controller is initialized, it my read the date from the service. A separate controller instance will be created for each view.
You may also check this blog to see, how data should be defined within a service, so that changing such data will auto-trigger controller to update view:
http://viralpatel.net/blogs/angularjs-service-factory-tutorial/
Notice: that service returns a reference to the array and controller saves this reference, when it is created.
Ok so I realized my problem was due to the fact that I was misunderstanding how $scope works in Angular not how $route.reload() was working. This topic
$scope variable not changing in controller when changed using directive
and this link in that question
https://github.com/angular/angular.js/wiki/Understanding-Scopes
helped me understand what I was doing wrong. $route.reload() and $templateCache.remove() were working correctly. The problem was that my $scope variable wasn't updating properly because each view was creating a different scope. Paraphrasing one of the links
The $scope is not the model.
The function of the controller is to write from the model to the scope
The function of the view is to display the $scope and write to the model if needed
I solved the problem using the controllerAS syntax discussed in the answers like this,
.controller('LivePreviewController',['$scope', function($scope) {
var vm = this;
vm.form_name = 'temporary name';
}]);
<!-- form_page.html -->
<div ng-controller="LivePreviewController as ctrl">
<input type="text" ng-model="ctrl.form_name>
And in my styled form partial view
<!-- styled_form.html -->
<div> {{ctrl.form_name}} </div>
This causes everything to work as expected

How would I create something similar like this in AngularJS

Having a hard time grasping AngularJs.. How would you build something like this simple textfields and animations in AngularJS? I had been looking into using directives but it just isn't adding up to me much. I had been trying to base this from what I learned online but was not having much luck
http://codepen.io/yusufbkr/pen/RPBQqg
HTML:
<div class="materialContainer">
<div class="title">LOGIN</div>
<div class="input">
<label for="name">Username</label>
<input type="text" name="name" id="name">
<span class="spin"></span>
</div>
<div class="input">
<label for="pass">Password</label>
<input type="password" name="pass" id="pass">
<span class="spin"></span>
</div>
<div class="button login">
<button><span>GO</span> <i class="fa fa-check"></i></button>
</div>
Forgot your password?
<div class="title">REGISTER</div>
<div class="input">
<label for="regname">Username</label>
<input type="text" name="regname" id="regname">
<span class="spin"></span>
</div>
<div class="input">
<label for="regpass">Password</label>
<input type="password" name="regpass" id="regpass">
<span class="spin"></span>
</div>
<div class="input">
<label for="reregpass">Repeat Password</label>
<input type="password" name="reregpass" id="reregpass">
<span class="spin"></span>
</div>
<div class="button">
<button><span>NEXT</span></button>
</div>
just use the codepen link ( http://codepen.io/yusufbkr/pen/RPBQqg ), stackoverflow won't let me input the rest of the code...
Thanks! Any help would be incredible
I'm just learning Angular myself. From what I know, you're on the right track to be thinking of directives for swapping out interactive parts of the DOM. I suspect you'll also be needing ui-router for dealing with the widget outcomes (I hear it's the industry standard, vs the built-in ngRouter).
I'm partly writing this out to make sure I understand it, so I hope other, more knowledgeable people will come by and answer any questions you have about it (or point out where I'm misundertanding something.)
So you bring the webpage into the Angular world by, for instance, making the <html></html> tags into <html ng-app="yourApp"></html>. The unchanging html that will be the same on every view is in the index.html file. Put the html for the boxes is in another html file, say box.html. On the index.html file in the place where you want to have your boxes appear, put <div ui-view></div>. The ui-view connects to ui-router. Down at the bottom of the index.html right before closing the body tag, put
<script src="lib/angular/angular.js"></script>
<script src="lib/angular-ui-router/release/angular-ui-router.js"></script>
<script src="app/app.js"></script>.
In app.js, you put something like,
angular.module('yourApp', [
'yourApp.box',
'ui.router'
])
.config(function($stateProvider, $urlRouterProvider, $httpProvider) {
$urlRouterProvider
.otherwise('/signin/child');
$stateProvider
.state('box', {
url: '/signin',
templateUrl: 'app/box/box.html',
controller: 'BoxController'
})
.state('box.child', {
url: '/child',
template: '<span>{{ definedProperty }}</span>'
});
})
});
Starting at the top, you name angular, call its module function, and give it the parameters ('theNameYouChose', ['dependencies','required'])
In the config bit there, you're telling it about dependencies config needs. $stateProvider and $urlRouterProvider are ui-router things, and $httpProvider is, I believe, an angular thing that is still necessary.
In the urlRouterProvider, you're providing the default address the website goes to: in this case it happens to be a template within a template, /signin from the signin state, and /child from the specifics pasted into signin
In the states, you are giving names and properties to the URLs that will be part of your app: like, if someone goes to index.html/signin, the 'signin' state is summoned.
The template within a template might be how you'd get the box variability that you're looking for. In box.html you will have another <div ui-view></div> and that's where the varying thing described in 'signin.child' gets put in.
box.html is also the place you put your specially created html tags, the ones you will make with the directive. I'll talk through those below.
The parent template, 'signin', talks about BoxController. Let's say you built that in box.js. It would look like this:
angular.module('yourApp.box', [])
.controller('BoxController', function ($scope) {
$scope.definedProperty = 'reRegPass',
$scope.arrayOfObjects = [{prop: 'red'},{prop: 'blue'}]
})
.directive('specificBox', function(){
return {
restrict: 'EA',
templateUrl: 'app/box/box-guts.html',
replace: true,
scope: {
source: '='
},
link: function(scope, element, attribute) {
element.on('click', function() {
alert('Functionality in specificBox is working');
}
}
}
});
The first line is again summoning angular module, and then naming it: note we already listed this, 'yourApp.box', as a requirement in app.js. Then we have the controller-naming function, and the appearance of specific properties in their $scope.
The directive is taking the more useful of the two forms a directive can take. They can either return functions (with signatures like the one in .link), or they can return objects that describe new HTML entities. This one is named specificBox, which Angular will translate into <specific-box></specific-box>. This object (technically called a 'directive definition object') can be translated into HTML tags because of the restrict property... 'E' is for element. 'A' is for attribute. I don't know how something that can be an element could also be an attribute, but having both options works, so I'm going with it. (Maybe for your various signin boxes you want a directive that has just 'A' to make new types of attributes.)
You put these specifically-crafted tags into box.html, and everything in box-guts.html will be between those tags... In fact, replacing those tags because of the replace: true.
The .link property is where you would put a function that would do something interesting to that element; turn it red if clicked, whatever. I have an alert example. Uses jQuery Lite for events.
The .scope property is odd. I believe if left off, the specific-box stuff would have the same controller as box (ie, BoxController), with box's scope and dependencies. But here, instead, we're making an isolate scope. box-guts will not depend on anything or have access to anything built before. How will it get interesting changeable data, then..? Every property in the scope object becomes an attribute on specific-box.
The '=' means that you will pass it objects from somewhere, and they will have 2-way data binding (changes made in box-guts will be reflected in the object collection in your app). Other options are '#', one-way data binding, which means you're passing box-guts a string or something that, if it changes in the DOM, you don't care to reflect in your app; or '&' to give it a function from a controller somewhere else in your app.
So ultimately, in box.html, you will have something like
<div class="box-holder">
<div ui-view></div>
<specific-box source="thing" ng-repeat="thing in arrayOfObjects"></specific-box>
</div>
Like I said above, ask questions and maybe we can sort this out.

AngularJS: How to provide independent communication between multiple instances of the same 2 controllers?

I have two controllers allocated to two views:
[ResultsView ng-controller="ResultsCtrl"]
[SearchView ng-controller="SearchCtrl"]
The Search View has many complex filters/options and is filled in by the user, then he/she can press "Search" on SearchView and Results should be populated into a Grid.
Now I can send information between two either by a Service or by using $rootScope.$broadcast.
Heres the problem I've run into:
[ResultsView ng-controller="ResultsCtrl"][SearchView ng-controller="SearchCtrl"]
[ResultsView ng-controller="ResultsCtrl"][SearchView ng-controller="SearchCtrl"]
[ResultsView ng-controller="ResultsCtrl"][SearchView ng-controller="SearchCtrl"]
If I were to have multiple Result-Search sections on the same page, how can I ensure they each act independently from each other? Using the Service approach, the ResultsCtrl and SearchCtrl both have the defined service
.controller("searchCtrl", ["$scope", "$searchHttp", function ($scope, $searchHttp) {
.controller("resultsCtrl", ["$scope", "$searchHttp", function ($scope, $searchHttp) {
So I can't change how each instance of the controller behaves regarding the service. Soon as one SearchCtrl calls the service, it will modify every ResultsCtrl instance.
Likewise using broadcasts $rootScope.$broadcast("searchResults"... will be picked up by every ResultsCtrl instance.
So whats the best way around this? I want to reuse the Results and Search View code since its basically the same. But I need to render each pair independently on the same page a few times.
I think the HTML structure you need is something like this.
<!--First-->
<div ng-controller="SearchCtrl">
<div ng-controller="ResultsCtrl">
</div>
</div>
<!--Second-->
<div ng-controller="SearchCtrl">
<div ng-controller="ResultsCtrl">
</div>
</div>
This HTML structure would help you to use independently the search results one's parent SearchCtrl created in ResultsCtrl.
jsfiddle is here.
I hope this would help you. :)

how to process html using angular and bindings (?)

here is my testing page :
<div ng-controller="test">
<input ng-keyup="asIwrite($event)" />
<p id="entityContent"></p>
</div>
and my controller :
EntitiesApp.controller('test', ['$scope', function ($scope) {
$scope.asIwrite = function ($event) {
$('#entityContent').html($event.srcElement.value);
}
}]);
this is actually working, if i write click in the input, the paragraph will hold the clickable url (processed html).
I am using jQuery to update the content of the paragraph as to show html element as they render in the page..
But using jQuery is a work-around in my opinion.
I want to achieve that using just Angular. How can I do ?
note : sure I can sweep jQuery off my project and use innerHTML of the
element but I want to know the way of doing that in Angular with
bindings. I used ng-bind on a paragraph element but that just
render the value of the textarea as is, no html process is performed.
See working example below.
You are right by doubting using jQuery is the right thing to do, and as you would expect it is not. The angular way to do that is register your input into the scope using ng-model, and the way to display it is using the ng-bind-html directive. (or simply ng-bind if it was simple text with no HTML)
However, Angular will not allow HTML binding by default as it could be a security issue. If you are sure about what you write, you can use $scope.trustAsHtml as showed in my example.
angular.module('test', [])
.controller('test', ['$scope', '$sce', function ($scope, $sce) {
$scope.trust = function(content) {
return $sce.trustAsHtml(content);
}
}]);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="test" ng-controller="test">
<input ng-model="content"/>
<p ng-bind-html="trust(content)"></p>
</div>
You probably have tried to do something like this but the content doesn't come out as html
<input ng-model="input" />
<p id="entityContent" ng-bind-html="input"></p>
You need to ensure strict context escaping as described here:
https://docs.angularjs.org/api/ng/service/$sce
Please keep in mind what you're saying you want to do is explicitly prevented by angular developers as a way to mitigate XSS attacks
You don't need to do that, angular is simplier than that.
<div ng-controller="test">
<input ng-model="variable1" />
<p id="entityContent">{{variable1}}</p>
</div>
Angular binds variables automatically. When the input's value change, the new value will be printed.

Categories