I'm trying to create new ngControllers on the fly, is this possible? I'm not writing it into the template with a ngRepeat or anything, since the user might never create an instance of a certain ngController.
Here's an example of my current template:
<div ng-controller="ViewUsers">
<div class="viewUsersList">
<table>
<tr class="userRow" ng-click="viewUser(user.user_id)" ng-repeat="user in users">
<td>{{user.first_name}} {{user.last_name}}
<br />{{user.phone}}
</td>
</tr>
</table>
</div>
</div>
So far it works great. When the user clicks a .userRow it calls viewUser(ID). The problem is here:
I want to create a new block of code like this:
<div ng-controller="UserDetail">
{{first_name}}
</div>
And append this to the DOM.
So if they click on Bobby and Sally, two UserDetail controller objects would be added to the DOM and work accordingly. (Ideally, I'd like to pass in the data-bound user model, but that's for later).
I tried a ghetto version in JSBin, but I'd rather not use JQuery if possible:
http://jsbin.com/EdOteTav/2/edit
Can anyone shine some light on this? Thanks in advance
I would change the code little bit and use directive instead of controller. See this simple and quick example in jsfiddle.
The directive would handle the click events and display whatever you want to display on the DOM. You can get the full user object from scope.user inside the directive.
Related
The title might seem weird but what im talking about is basically what this link is doing.
Im look for a way to use the current iterated person in a <tr> outside of the *ngFor scope.
In the link he used ng-repeat-start and ng-repeat-end to include multiple tag withing the ng-repeat. How can i achieve the same behavior withing Angular2 using *ngFor?
I had to use the <template> tag, which made the iterated objected accessible within it.
<ng-template let-person ngFor [ngForOf]="people">
<tr>
<td>{{person.name}}</td>
<td>{{person.gender}}</td>
</tr>
<div>
<span>{{person.details}}</span>
</div>
</ng-template>
Hope it'll help somebody
The * in *ngFor (and really any angular directive) is shorthand that tells angular to surround the element in an ng-template, which is just the angular implementation of . You can read about how that happens at https://angular.io/guide/structural-directives, but I'll reproduce the relevant example below.
If your code contains
<div *ngIf='object'>{{object.property}}</div>
as part of the render process, angular transposes that to
<ng-template [ngIf]='object'>
<div>{{object.property}}</div>
</ng-template>
I have a site showing a list of comments and the comments can be liked by clicking a button.
The site is written using angular and the list of comments are fetched from a node back end and rendered using ng-repeat of Angular.
To record a like I need to pass some information related to the comment to the node endpoint via AJAX.
The problem is that I don't wanna store any information related to the comment on the DOM. And I did not get how I can use jQuery.data here since I am 'ng-repeat'ing to render the comments.
Please suggest some alternatives..
Here is how the comments are rendered
<div class="stickyNotes" ng-repeat="comment in commentList1">
<p>
{{comment.commentContent}}
</p>
<div>
<img src="../resources/images/thumb.png" class="upvoteImage" />
</div>
</div>
You can use $index, and pass it to your controller method onClick. Then you can access the exact comment $scope.commentList1[index] on your controller, and do what you what with it.
you can learn about $index here:
https://thinkster.io/egghead/index-event-log
UPDATE: After some very insightful code from #Marc Kline, I went back and cleaned up my page. It turned out that I had my controllers listed in reversed (My angular controller was inside the Isotope controller, instead of the other way round). Once I changed it back and cleaned off some additional scripting, it started working again. I have updated the code snippet to reflect the change. Thanks to Marc and S.O!
I am having trouble figuring out how can I add new items using Angular and still let Isotope manage their UI display.
I am using Isotope and Angular to render server results in a masonry style layout. When I add new items to the layout on a button click, angular adds it just fine. However, they do not appear in the context of the isotope UI and appear separately (and cannot be sorted, laid out or filtered using Isotope).
Here is my JS Code
<!-- Define controller -->
var contrl = app.controller("MainController", function($scope){
$scope.items ={!lstXYZ}; //JSON data from server
//Function called by button click
$scope.addItem = function(item)
{
$scope.items.push(item);
$scope.item = {};
}
});
contrl.$inject = ['$scope'];
Here is the HTML to display the server results...(Updated to show working code..refer comments)
<div ng-controller="MainController">
<div class="isotope" id="isotopeContainer">
<div ng-repeat="item in items">
<div class='element-item {{item.status}}' data-category='{{item.status}}'>
<p class="number">{{item.type}}</p>
</div>
</div>
</div>
</div>
And here is my HTML button to add the new items
<table>
<tr>
<td><input type="text" ng-model="item.status" /></td>
</tr>
<tr>
<td><input type="text" ng-model="item.type" /></td>
</tr>
<tr>
<td colspan="2"><input type="Button" value="Add" ng-click="addItem(item)" /> </td>
</tr>
</table>
I am not sure how do I ensure that Isotope can recognize the newly added element and re-animate the layout.
Any help or pointers will be very appreciated. Thanks!
ng-repeat takes care of adding the new element to the DOM for you. However, Isotope isn't doing any "watching" for you - you have to manually invoke a redraw of the container.
You could just add something like $("#isotopeContainer").isotope(...) directly to your controller, but in the spirit of keeping your controllers lean and free of DOM-related code, you should instead create a service. Something like:
myApp.service('Isotope', function(){
this.init = function(container) {
$(container).isotope({itemSelector: '.element-item'});
};
this.append = function(container, elem) {
$(container).isotope('appended', elem);
}
});
... where the first method initializes a new Isotope container and the next redraws the container after an item is appended.
You could then inject this service into any controller or directive, but directives probably are best for this scenario. In this Plunker, I created two new directives: one for Isotope containers and another for Isotype elements, and used the service to do the initialization and redrawing.
In this particular case, my code was not written correctly. I have updated the question's code but wanted to mention it more clearly here...
Apparently, the beauty of Angular is that you do not need to bother with the underlying UI framework (Isotope in this case). As long as you update the Angular data array, the UI binding is updated automatically.
The only gotcha is to ensure that the UI framework div is within the context of your Angular div.
Here is the non-working code...Note that the isotope div is outside the Angular controller.
<div class="isotope" id="isotopeContainer">
<div ng-controller="MainController">
<div ng-repeat="item in items">
<div class='element-item {{item.status}}' data-category='{{item.status}}'>
<p class="number">{{item.type}}</p>
</div>
</div>
</div>
</div>
Here is the updated code with isotope running within the Angular controller context...
<div ng-controller="MainController">
<div class="isotope" id="isotopeContainer">
<div ng-repeat="item in items">
<div class='element-item {{item.status}}' data-category='{{item.status}}'>
<p class="number">{{item.type}}</p>
</div>
</div>
</div>
</div>
Hope this helps. I am thankful for all the responses and help I got from SO. Appreciate the learning opportunity.
Hey all I'm trying to get a feel for angular and have ran into a little snag.
I have a container structure like the following:
<div class="main-container" ng-view>
<!-- The below divs are constantly being
reloaded based on the current URL and it's associated view -->
<div class="left-col">
</div>
<div class="right-col">
</div>
</div>
Before I implemented Angular I just had a simple script that would check the height of the window and set the height of the left column and right column divs accordingly.
With angular there is probably a better way to do this then attaching an event function to the window object. Basically I want to fire a function everytime a new view is rendered but not duplicate the below code in all of my angular controllers like so:
$scope.$on('$viewContentLoaded', setColumnHeight);
Any help would be appreciated. Thanks!
I've solved this problem in my app by having a root "Application Controller" at the top of the DOM tree. For example:
<body ng-controller='applicationController'>
...
<div class="ng-view"></div>
...
</body>
applicationController is always there, and can set up bindings on its scope when it is created.
How about putting that event listener on the root scope:
$rootScope.$on('$viewContentLoaded', setColumnHeight);
You could use something like this:
<div class="main-container" ng-view>
{{ setColumnHeight() }}
<!-- The below divs are constantly being
reloaded based on the current URL and it's associated view -->
<div class="left-col">
</div>
<div class="right-col">
</div>
</div>
and it will check the height every time a render is made, however, this seems to be a bit over processing.
The best approach would be to only update the height from both columns when the height of one of them changes. For that you could use DOM Mutation Observers, however they are not yet available on all browsers. If that's not a problem for you, check the mutation-summary lib.
If you are using jQuery you can also try this: https://github.com/jqui-dot-net/jQuery-mutate (examples here).
I am a super beginner to EE and was literally thrust into managing my company's website that is built in EE without training. I'm not a programmer, I'm a designer, so it's been taking me awhile to plug through this. So I might need some dumbed down language :)
I want to create a page that has some Javascript on it. Do I need to create a new template JUST so I can put some javascript on it? And how do I communicate to EE that I want the page I created to go with that template?
I duplicated the page/index template and renamed it to clinician-map (the same name of the page I created in the publisher). EE didn't like that and the page subsequently broke. All I want to do is insert one javascript item, this seems way too inefficient for just one page. Help??
(using EE 1.6.8)
Here is my code from clinician-map template.
{assign_variable:my_weblog="page"}
{assign_variable:my_template_group="page"}
{embed="embeds/html_head" url_title="{segment_2}"}
{embed="embeds/html_styles"}
{embed="embeds/html_scripts"}
<?php include_once("analyticstracking.php") ?>
</head>
{exp:weblog:entries weblog="{my_weblog}" disable="categories|member_data|pagination|trackbacks" limit="1" sort="asc" }
<body class="{url_title}">
{/exp:weblog:entries}
<div id="wrapper">
{embed="embeds/html_headerPlusLeftNav"}
<div id="content">
<div id="contentMain">
{exp:weblog:entries weblog="{my_weblog}" disable="categories|member_data|pagination|trackbacks" limit="1" sort="asc"}
<h2>{title}</h2>
{page_body}
{/exp:weblog:entries}
<!--contactforminfo -->
{exp:weblog:entries weblog="{my_weblog}" disable="categories|member_data|pagination|trackbacks"}
{related_entries id="playa_contentcalloutitems"}
<div class="callout">
<h3>{title}</h3>
{callout_summary}
</div>
{/related_entries}
{/exp:weblog:entries}
{exp:weblog:entries weblog="{my_weblog}" disable="categories|member_data|pagination|trackbacks"}
{related_entries id="playa_contentfeatureditems"}
<div class="featuredContent">
<h3>{title}</h3>
{exp:word_limit total="50"}
{contentfeatured_summary}
{/exp:word_limit}{if contentfeatured_body!=""}<p><a href='{url_title_path='content-featured/'}' class='more'>Read More</a></p>{/if}
</div>
{/related_entries}
{/exp:weblog:entries}
</div>
{exp:weblog:entries weblog="{my_weblog}" disable="categories|member_data|pagination|trackbacks"}
<div id="contentSub">{related_entries id="playa_contentsubitems"}<div class="item {contentsub_bgcolor}">
{if contentsub_contenttype=="Text or Picture with Text"}
<h3>{title}</h3>
{exp:word_limit total="50"}
{contentsub_summary}
{/exp:word_limit}{if contentsub_body!=""}<p><a href='{url_title_path='content-sub/'}' class='more'>Read More</a></p>{/if}
{if:else}
<h3 class="imgHeader">{title}</h3>
{exp:html_strip convert="y" convert_back="none" keep="a,img"}
{contentsub_summary}
{/exp:html_strip}
{/if}
</div>{/related_entries}
{/exp:weblog:entries}
{embed="embeds/html_mailingListSignup"}
</div>
</div>
{embed="embeds/html_footer"}
</div>
</body>
</html>
At glance I can see a couple things that might be confounding you...
You started with a template called 'index' in the 'page' template group.
Looks like the 'page' template you are starting from is meant to display a single entry from the 'page' weblog.
So a request url might look something like this:
http://example.com/page/some_url_title
where 'some_url_title' is the 'url_title' value one of the entries in your 'page' weblog.
Now you have gone and duplicated the index template and called this new template 'clinician-map'.
So you would call an entry through this template at:
http://example.com/page/clinician-map/some_url_title
Now, notice that the first url had 2 segments, while the second had 3 segments?
That's not normally a big deal but the fellow who designed the index template did something that makes it problematic. He is taking the value of segment_2 and passing it through an embed.
So in the first example (index) we are passing the dynamic value "some_url_tile" while in the second example (clinician-map) we are passing "clinician-map". If the embedded template 'html_head' is expecting to get a valid url_title but instead gets the string 'clinician-map' you are likely going to get unexpected results.
Also I don't think we know enough about what you are trying to do to decide if creating a new template is the right approach here. It may be that what you actually need is a new weblog entry or perhaps just a dynamic value inside your existing template.
If it did turn out that a new template is the best approach you could fix the problem I have described by simply replacing segment_2 with segment_3, but I am by no means certain that that is the way you want to go.
I want to create a page that has some Javascript on it. Do I need to
create a new template JUST so I can put some javascript on it?
More specifics would be needed in order to give a solid recommendation but in almost every case, I recommend keeping JavaScript grouped together either in the <head></head> or ideally right before the closing </body> tag if you can get away with it.
Looking at your template code, it appears all the JavaScript is stored in the embeds/html_scripts page. I would add the JavaScript you need to that template. If you only want the JavaScript to appear for certain pages only, I would make use of a conditional (which I'll outline at the end of my answer).
And how do I communicate to EE that I want the page I created to go
with that template?
ExpressionEngine URLs (by default) are assembled as follows:
http://website.com/group/template/url_title
Therefore if you have a page with a url_title of "contact-us", and you wanted that page to use a template in site/pages, you could tell your page to use that template like so:
http://website.com/site/pages/contact-us
That url is obviously fine and dandy for blog articles and such, but it's not that pretty; so ExpressionEngine also enables you to construct "page" based navigation which creates navigation tree based url structures, such as:
http://website.com/contact-us
There are a few third party modules that make it easy to build page navigation such as:
devot-ee.com/add-ons/structure
Using Structure, you specify the default template for each channel and can override the template for each page as well.
I duplicated the page/index template and renamed it to clinician-map
(the same name of the page I created in the publisher). EE didn't like
that and the page subsequently broke. All I want to do is insert one
javascript item, this seems way too inefficient for just one page.
Help??
(using EE 1.6.8) Here is my code from clinician-map template.
There are a number of things I would do different in regards to the template code you provided; however, as a quick fix here's how I would add the one line of JavaScript,
1) Open the embeds/html_scripts template and add the following logic:
{if segment_2 == "my_page_url_title"}
<!-- javascript here -->
{/if}
Note: Here's how segments are determined:
http://website.com/segment_1/segment_2/segment_3
Okay. I ended up just creating a new webblog and new template group and finally it seems like it's working. My javascript is not, but I can figure that out.
Thank you so much for your patience with helping me!