When I read the documentation for BindingContext on KnockoutJS website, it has an code example like below to illustrate $data:
<ul data-bind="foreach: ['cats', 'dogs', 'fish']">
<li>The value is <span data-bind="text: $data"></span></li>
</ul>
I am pretty new to KnockoutJS, I wonder is putting a direct array after foreach binding a valid usage? And also in above case, it seems you can omit using applyBinding() to activate. So I guess such syntax is only for illustration purpose, it is not a valid code like what could be in a real KnockoutJS application.
If someone has had similar thoughts and is assured, could you either confirm or correct me?
To answer your first question
'I wonder is putting a direct array after foreach binding a valid usage? '
Yes it is just for illustration purpose they have put the code there. And if you put the code in sinppet code will not work because
They do not want to illustrate foreach here in fact they are giving example of $data.
For foreach documentation take alook here
To another your another question
'it seems you can omit using applyBinding() to activate'
No you cannot omit the applyBinding.
Look in following sinnpet code will not give you desired output unless add ko.applybinding in your code.
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<ul data-bind="foreach: [ 'Jan', 'Feb', 'Mar', 'etc' ]">
<li>
The current item is: <b data-bind="text: $data"></b>
</li>
</ul>
<script type="text/javascript">
//ko.applyBindings();
</script>
As quoted in Knockout documentation
Activating Knockout
The data-bind attribute isn’t native to HTML, though it is perfectly
OK (it’s strictly compliant in HTML 5, and causes no problems with
HTML 4 even though a validator will point out that it’s an
unrecognized attribute). But since the browser doesn’t know what it
means, you need to activate Knockout to make it take effect.
To activate Knockout, add the following line to a <script> block:
ko.applyBindings(myViewModel);
Related
NOTE: I'm a new member here so I couldn't directly comment and ask for clarification.
So, my question is: How can I work around ng-if creating a child scope for a select element?
I have the following code:
HTML
<select id="project-select"
ng-if="projects.length > 0"
ng-options="project.name for project in projects"
ng-model="currentProject"
ng-change="broadcastChange('project-changed', currentProject)">
</select>
And my controller is set up in the following format:
function Controller() {
//Do code stuffz
}
angular
.module('app')
.controller('Controller', Controller);
I learned from this post that the "ng-if" is creating a child scope.
So even when the model changes, this part stays the same because it is a primitive value: (name is just a string)
<div id="current-project" class="pull-left">
<strong>Project: </strong>{{currentProject.name}}
</div>
Furthermore in the aforementioned post there were a couple options.
a. Simply change to this: ng-model="$parent.currentProject" which feels a little hacky
b. Set the object value in the controller, which I'm not entirely sure how to do. I feel like it's an easy fix, but I'm somehow overcomplicating it.
Anyway, for now I simply changed ng-if to ng-show and that's solved the problem. However, I am trying to understand Angular more deeply and I feel like this issue could be explained a little bit better to me. Thanks in advance!
What you will find with Angular scope variables is: always use a dot.
That's the mantra from the excellent ng-book
In your case, what this means is this:
You have this code:
<select id="project-select"
ng-if="projects.length > 0"
ng-options="project.name for project in projects"
ng-model="currentProject"
ng-change="broadcastChange('project-changed', currentProject)">
</select>
Which means that you are binding to a $scope variable called $scope.currentProject.
Because of the mysterious and awesome way that javascript works, this does not get updated when you are inside of a child scope.
Thankfully, the solution is actually quite simple. Instead, create an object like so:
$scope.myData = {
currentProject: ''
}
And in your markup, bind to that like so:
<select id="project-select"
ng-if="projects.length > 0"
ng-options="project.name for project in projects"
ng-model="myData.currentProject"
ng-change="broadcastChange('project-changed', myData.currentProject)">
</select>
And voila. It will update, even though it's in a child scope.
This is actually quite useful, because you now have a way to "meaningfully" group variables together. Here's some other pseudo-code to demonstrate what I mean:
$scope.projectData = {
currentProjectID: 1,
currentProjectTitle: 'My Cool Project',
projects: [
{id: 1, name: 'My Cool Project'},
{id: 2, name: 'Another Project'}
],
someOtherProperty: false
// ...etc....
}
As a side-note, this section of this article might be helpful: http://docstore.mik.ua/orelly/webprog/jscript/ch11_02.htm#jscript4-CHP-11-SECT-2.1
If all you want to do is show/hide the select element based on the projects in your 'Controller' controller scope, then ng-show is the right way to go here. In my experience, I've used ng-if when I'm conditionally loading a larger "partial" view containing numerous controls where I felt a separate scope was necessary to avoid having a very large scope (or to facilitate re-use).
You are correct. Do not use $parent in any production Angular apps. It makes your model dependent on the structure of your view, which makes your code hard to refactor and less modularized.
Binding to object properties is the way to go, as you suggested in your "b" answer. The recommended way to do this in the latest version of Angular 1.x is by using the "controller as" syntax. This method makes use of "prototypical inheritance" in javascript. There is a good explanation on this here: http://javascript.info/tutorial/inheritance
I created a plunker for you to demonstrate how binding to object properties works in nested scopes. take a look at the "controller as" syntax". also, try changing the value of ctrl.testBinding in the input, you will see that reflected in the ng-if child scope. I will try to find some links to explain this in more detail.
https://plnkr.co/edit/Gx5xbkJXgzjPSG8kajPR?p=preview
<!DOCTYPE html>
<html >
<head>
<link rel="stylesheet" href="style.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body ng-app="testApp">
<div ng-controller="testCtrl as ctrl">
<input ng-model="ctrl.testBinding" type="text"/>
<button ng-click="ctrl.toggle()">toggle show</button>
<div ng-if="ctrl.show">
{{ ctrl.testBinding }}
</div>
</div>
</body>
</html>
//script.js
function testController($scope) {
var vm = this;
vm.show = true;
vm.toggle = function(){
vm.show = !vm.show
}
}
angular
.module('testApp', [])
.controller('testCtrl', testController);
I am trying to conditionally display a directive based on a boolean value stored in the parent scope. I can't figure out why the below does not work. By, "not work" I mean neither directives are displayed.
<ul class="nav navbar-nav pull-right" ng-switch="userIsAuthenticated">
<account-item item="accountMenuItem" ng-repeat="accountMenuItem in anonymousMenuItems" ng-switch-when="false"></account-item>
<account-item item="accountMenuItem" ng-repeat="accountMenuItem in authenticatedMenuItems" ng-switch-when="true"></account-item>
</ul>
Neither directives are shown even thought "userIsAuthenticated" is set to 'false' in my test case. If I add {{userIsAuthenticated}} above the directives 'false' is output as expected.
I've also tried this:
<ul class="nav navbar-nav pull-right" ng-switch={{userIsAuthenticated}}>
<account-item item="accountMenuItem" ng-repeat="accountMenuItem in anonymousMenuItems" ng-switch-when={{false}}></account-item>
<account-item item="accountMenuItem" ng-repeat="accountMenuItem in authenticatedMenuItems" ng-switch-when={{true}}></account-item>
</ul>
If I remove the conditional ng-switch-when attribute from either of the directives they will display. So I'm know the problem is my ng-switch.
Your usage of ng-switch works in this simplified demo, of course without your account-item directive:
http://plnkr.co/AppN8xmFeIwjaP631lj7
Without seeing the code for account-item, it is hard to guess what might be interfering with it. You might consider using ng-if to handle displaying one item or another.
<ul>
<div ng-if="!userIsAuthenticated">Content when not authenticated</div>
<div ng-if="userIsAuthenticated">Content when authenticated</div>
</ul>
Update
Also make sure you bind to an object property, instead of a primitive boolean. Like: user. authenticated
Since ngSwitchWhen has a priority of 800, you need to set a higher priority to your custom directive (i.e. account-item) in order for it to be compiled before being process by the ngSwitchWhen directive. E.g.:
.directive('accountItem', function () {
return {
...
priority: 900,
...
};
});
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!
I'm trying implement a way to recursively template using jsRender. The issue is, my data object has a unary self-referencing heirarchy which requires recursive looping to show all of the attributes. The example here is a forum post which can contain any number of reply posts. Each reply post can contain any number of children posts and so on. I have the following code working except for the recursion part. I could only find one remote reference to this via the Googles, so here is what I have thus far:
<script id="forumPostsTemplate" type="text/x-jsrender">
<article class="forumPost">
<header class="forumPostHeader">{{:PostSubject}}
<div class="info">By: Some Person, {{:CreatedDate}} Flag as innapropriate </div>
</header>
<div class="content">
{{:PostContent}}
{{for Replies}}
{{:Replies tmpl="#forumPostsTemplate"}}
{{/for}}
</div>
</article>
</script>
Does anyone have any experience with this sort of functionality? I am currently running the most recent version of jsRender if that helps.
Per this example for jsRender, does it work to call your template like this instead?
https://github.com/BorisMoore/jsrender/blob/master/demos/step-by-step/06_template-composition.html
{{for Replies tmpl="#forumPostsTemplate"/}}
These three different versions of a tree tag control illustrate exactly that kind of recursion:
http://www.jsviews.com/#samples/tag-controls/tree
I'm using jQuery templates to build a tree. It works very well so far, but I came across an issue when trying to determine if an item is at the root level or not.
I use a template similar to that of render tree items:
<script id="tree-row-tmpl" type="text/x-jquery-tmpl">
<li>
<div class="row ${NodeType}">
${Name}
</div>
{{if expanded}}
<ul>
{{tmpl($data.chidren || []) "#tree-row-tmpl"}}
</ul>
{{/if}}
</li>
</script>
Now in the link click handler I try to determine root node using:
if($.tmplItem(this).parent)
It turned out the root tmplItem.parent is not null (as I expected), but contains an object with two properties: {data:{}, key:0}.
I see that I can check item.parent.parent or one of the properties which exist in regular tmplItem and missing in the root object. But this seems like a kind of hack for me - I'd prefer finding an "official" way of verifying a tmplItem whether it's empty or valid.