The problem I am having is I need the root element as an anchor tag, then need a div under the anchor tag. They will both be using the same angular controller which belongs to the same app. The databind on vm.Open works find inside the anchor tag, but it is not working inside the div tag. How can I have the div tag also bootstrap as 'app' with the controller 'ordercontrol'?
Right now I have :
My HTML
<a data-ng-app="app" data-ng-controller="ordercontrol as vm" href="#" data-ng-click="vm.Open = !vm.Open">{{vm.Open}}</a>
<div data-ng-app="app" data-ng-controller="ordercontrol as vm" id="QuickOrderDiv">
<div class="row">
{{vm.Open}} //showing as '{{vm.Open}}' inside page
</div>
</div>
Per AngularJS documentation, only one application can be bootstrapped per HTML document. There is probably not a good reason for you to try and declare and use multiple apps in one document. Depending on your intention, there are a few ways to proceed.
First, remember: assigning a controller to an element (using the ng-controller directive) creates a new scope that inherits from the parent. All elements, directives, and additional controllers used within that scope can use that controller to share functionality. So in essence, a controller is used to centralize models and application logic that is specific to an application and usually to a view (think of any functional task in your application, such as an order form or log in page; those are serviced by controllers).
If you want to reuse a behavior multiple times throughout your application, and that behavior is not specific to a particular view in your application (think of a component, like a date picker, or perhaps a shopping cart status icon/link) you may wish to encapsulate your logic in a directive.
Now, there is some overlap (for example, directives can have a scope or controller of their own), and it may be confusing when to use one or the other. As I mentioned above, controllers are primarily intended to contain business logic for a view in your application. Directives are more orthogonal, encapsulated templates and logic that a) you can easily reuse across the views in your application, and b) extend existing HTML elements with richer mark-up and programmed behaviors. You can use a service (which is a single-instance object) to coordinate data between controllers and directives.
Another common issue new developers struggle with is maintaining or inheriting different states independently, considering that you can only have one ng-view element per document. In that case, consider ui-router.
My wild guess for your case, you may want some sort of QuickOrder directive that binds to a value on OrderController to determine whether or not it should display, and contains additional template mark-up for displaying the order or whatever and the logic to manage it.
Related
I am essentially trying to create html component blocks that can be used in a WordPress page editor. The issue that I am running into is each block is using the ng-controller directive to get access to the scope of said controller. This is resulting in new instances of the same controller being created which is causing a delay in the blocks showing up (it's also not a practical way of doing this).
The way that I have the app currently setup is to use a short-code linked to one large page of html, which is not easily manageable as an end user and moves away from the component based structure i'd like to implement. I've tried using the angularjs .component() directive but I am running into the same issue of having new controller instances.
<div ng-controller="someCtrl">
--- first html block that loads first ---
</div>
<div ng-controller="someCtrl">
--- second html block that is delayed until after the first is loaded ---
</div>
Essentially, what I would like to know is whether or not there is a way to access the same controller (excluding a wrapper) without creating new instances of said controller?
The answer is no. Each instance of a controller will be a new instance. You could leverage #TheUnknown's suggestion to share data across these instances, as a service is a singleton.
you could use the same controller, by using ng-if.
so when
<div ng-if="loadFirstHtmlBlock"></div>
is true. it will load only that particular <div>.
Same goes for
<div ng-if="loadSecondHtmlBlock">
This is just a simple method you could use.
The end result that I have found as the best way (in angularjs) to fix this sort of issue would be to add different components with their own respective controllers that fetch information from a service. This would then result in much better performance on the page as well as a more 'in sync' type of loading.
How and why is <body ng-app> used? How can we assign controllers, directives etc to this nameless module. Also explain how this is related to manually bootstraping the Angular App.
Fiddle
This is actually three separate questions, but I'm happy to tackle each one.
How and why is used?
Angular will not and cannot properly bootstrap the application unless there is an entrance point to that application. According to the documentation, if a parameter is not passed that names the app instance, angular will attempt to auto-bootstrap the application for you by crawling the DOM and using the first ngApp directive instance that it is encountered.
We typically want to place our entrance point on the <body> element to encompass all the potential DOM we need without cluttering it with <head> elements, such as loading scripts and css. That said, if you are auto-bootstrapping your application, the recommended placement is on the HTML element.
How can we assign controllers, directives etc to this nameless module?
Modules, controllers, etc MUST be attached to something in order for Angular to pick them up and interopt with them correctly.
Once the application is bootstrapped, Angular will begin parsing the DOM, looking for directives. If you have an application instance (and you do), your controllers will be automatically be added to that instance. If you look at the bootstrap documentation -> Automatic Initialization, you'll find the following:
Angular initializes automatically upon DOMContentLoaded event or when
the angular.js script is evaluated if at that time document.readyState
is set to 'complete'. At this point Angular looks for the ng-app
directive which designates your application root. If the ng-app
directive is found then Angular will:
load the module associated with the directive.
create the application
injector compile the DOM treating the ng-app directive as the root of
the compilation. This allows you to tell it to treat only a portion of
the DOM as an Angular application.
How the heck is the fiddle working?
This one is actually smoke and mirrors that really shouldn't count :). If you look at the network traffic for JSFiddle, you'll find that AngularJS is actually being loaded. As a result, your interpolation is actually getting automagically bound to the JSFiddle Angular instance, not one that you provide yourself (or in this case didn't), parsed, and subsequently rendered into the DOM as 2.
Per angularjs.org:
"The ngApp directive designates the root element of the application and is typically placed near the root element of the page - e.g. on the or tags."
In other words, ng-app is what makes your html become an Angular application, thus being able to use the {{ }} in your code. These brackets are able to carry out operations, which is why you're able to calcuate 1 + 1. If you were to use variables, however, you would need to attach an ng-controller to a container div and initialize a Controller.
It's the equivalent of ng-app="". However, you need a namespace to attach controllers, directives. etc too. Angular can interpolate {{ 1 + 1 }} without a namespace, but you won't be able to bind anything to the views scope. Interestingly enough ng-app=" " is a namespace you can bind to.
https://jsfiddle.net/n3hygcnd/3/
I am creating form, where few fields are dynamic, ng-model is added dynamically.
Ex.:
form.append("<input type='hidden' name='paymillToken' value='" + token + "' data-ng-model = 'formdata.token'/>");
This fields shows undefined while I try to access using $scope.formdata.token
Following is another scenario where I am adding fields via ajax.
angular.forEach(data.data, function(obj, key) {
list+='<div class="items text-center"><img src="assets/uploads/discs/'+obj.image+'" class="img-circle"><br><input type="radio" id="chkDisc'+obj.id+'" name="disc_id" value="'+obj.id+'" required data-ng-model="formdata.disc_id" /></div>';
});
$scope.discslist = $sce.trustAsHtml(list);
This model disk_id is not accessible too.
Okay, to expand on my comment and a bit more on what everyone else here is saying to you, the main issue you're having is inherent in your approach. The way you're trying to manipulate the DOM is very un-AngularJS.
In AngularJS, when you want to change what is displayed to the user (the view), you make changes to your model (your controller scope). That means, you have to set up your view to be able to respond to those changes. We do that with directives and expressions in Angular.
You're probably already using directives to respond to changes in your model whether you realize it or not. ngRepeat, ngModel, ngShow, ngIf, ngInclude, are a handful you're probably familiar with, and even forms and form elements like inputs are actually directives in Angular. When you use these, a change in your model (such as loading data into the controller scope) signals to Angular that it should check whether that change affects any of the directives in your view, and if so, respond to it by updating the view.
In order to do this, Angular needs to know which parts of the model are connected to which parts of the view. It makes these connections when it compiles the html elements that are added to the page. This compile process happens automatically when you load an Angular app. After that, it's up to us to tell Angular when to compile html that is added to the page.
More often than not, we do this without even realizing it. For example, when you use the ngView directive, it will compile the template for each route that it loads, so that all of the directives in your template are properly linked with their associated model.
I know this is a long explanation, but there are two very important points here that are essential to learning AngularJS:
To change the view, you change your model and let the directives (and expressions) on your page respond to those changes.
When you add html elements to the page, if you want AngularJS to be able to use them in your view, they must be compiled first. The compile process should be done via a directive (either a built in one or a custom one).
So, how does all of this apply to your question?
First, I'm guessing that you're breaking both rules by trying to manipulate the DOM via a controller. Even if it is possible to use $compile in a controller, using a controller to change the DOM is bad practice and simply wrong to do (read the part in that link to the doc that specifically states: Do not use controllers to: Manipulate DOM...). A good rule to remember when you're learning AngularJS is that the only time you should ever be using JQuery or JQLite inside Angular is when you are creating a custom directive.
Okay, so how do you solve your question? Use a directive. It looks like you've got a case where you're trying to iterate over an object called data and add some inputs that correspond to the data.data property. This sounds like a job for ngRepeat.
The first thing you need to do is add your data to your controller and make sure it is accessible to the view. The easiest way to do this is by injecting $scope into your controller and setting the data on a scope variable. In its simplest form, that might look something like this:
angular.module('MyApp', [])
.controller('MyController', ['$scope', function($scope){
$http.get('/some/url/that/returns/the/data').
success(function(data) {
$scope.data = data;
});
}]);
Now that we have the data somewhere that we can access from the view, we can use it with the ngRepeat directive in our html, something like this:
<div ng-controller="MyController">
<div class="items text-center" ng-repeat="disc in data.data">
<img ng-src="assets/uploads/discs/{{disc.image}}" class="img-circle"><br>
<input type="radio" id="{{'chkDisc' + disc.id}}" name="{{disc.disc_id}}" value="{{disc.disc.id}}" required data-ng-model="formdata[disc.disc_id]" />
</div>
</div>
This is a common issue. By updating the value and not the model angular has no idea that the value in the field has been updated. As the first commenter said updating in this manner is completely unnecessary when using ng-model.
I am developing a page that has a news feed on it; when I think of it logically, this feed has several states on it (news, settings, favorites, etc.).
A feed should be able to be on many types of pages, such as for a product or a person or whatnot, so the way I understand it is that the feed itself should have states because their parent page can have all sorts of other states that the feed does not care about, and vice versa.
I can't figure out how to accomplish this -- at first I thought that I would be able to accomplish this using a named view on the parent page but can't figure out if a view can have a state, or how I would code that.
How should I be implementing this structure?
What you are looking for sounds a lot like a directive:
A directive usually has an HTML template associated to it
A directive backs this HTML template with a scope that can (and usually should) be independent of the page scope, with only a few explicitly defined parameters (values and/or functions) passed to it
A directive can define specific behaviours and functions, just as a regular page would via its controller
A directive can nonetheless communicate with its parent page, whether because it shares its scope (usually a bad idea) or because there is two-way binding of select fields
Optionally a directive can communicate with other directives (in your application, this could be useful if you define a "rss feed" directive and a "rss news item" for example)
The states would be variables, some of which you pass to your directive from your page, some you compute inside the directive. They would in turn parameterize the directive HTML template with the usual angular behaviours (e.g. ng-hide/show some subcomponents).
Angular directive doc : https://docs.angularjs.org/guide/directive
A good tutorial : http://www.sitepoint.com/practical-guide-angularjs-directives/
Does that answer your question ?
I'm building a complex, variable layout with infinite scroll UI in AngularJS.
Think something like Flipboard with a load of repeating data items, each item containing the same thing (title, description, image etc). Each item is pre-loaded into JSON, sorted and then organised into a series of pages, selected from a series of variable layouts. Each page has a variable number columns and each column a variable number of rows.
In order to achieve this, I'm using multiple Directives for each page layout, compiled at run time. Inside each layout Directive, I'm then building a variable number of columns, each of which contains an ng-repeat for the variable number of rows. Each row can be a different Directive, depending on how it needs to be styled. Again, all of this is happening at runtime.
I'm 99% of the way there.
The Directives are being compiled correctly and calling up the correct column Directives, which in turn are compiling the correct number of rows. Once we get to the row/item level, I'm using a Service which brings back the correct Directive for each item. The reason I choose the Service approach is that I want the items to be reused inside other modules.
The problem I'm having, is that once the page layout is compiled, it sets up the columns and then the rows but does not execute the ng-repeat. I need to get the ng-repeat to loop and also call the specific Directives I use for each item.
I think is a problem to do with $compile and what Angular knows about the DOM. Maybe I need to do a $compile or $apply at some stage to get the ng-repeat to kick in and bind the final Directives to the data items.
**
Side note: if I don't use a Service, but simply use define each layout Directive with a templateUri it works perfectly!
I can go with this approach, but ideally, I'd like to get away from downloading a bunch of templates at runtime. Also, by passing parameters into a Service, I can design more layouts, much quicker and easier than having to build individual template files.
I've set up a JS fiddle with quick example of how I'm approaching all of this:
http://jsfiddle.net/joecarney/vE3Ls/6/
Some SO required code just to post this comment:
<div ng-app="app" ng-controller="appPageController">
<div>
<div my-page></div>
</div>
</div>
If you use scope: {} in a directive it creates a new scope so your directives will not have access to controller scope unless you allow it, which you started out doing but appears you then copied and pasted the same thing for all :)
The directive compiling items in ng-repeat had no access to items. I removed scope from directives and used the attrs value to access the attributes
http://jsfiddle.net/vE3Ls/7/