Knockout templates with same name and different templateUrl - javascript

I have the following problem.
I have two templates with the same names that constitute a editable and a readonly variant of the same template.
Two different places in my html (that is created dynamically, but it is this condition that gives me the problem) I use the template binding, and I want to bind to each of these.
The first one will then look like this:
<div data-bind="template: {name: 'myTemplate', data: $data, templateUrl: '/Templates/readonly/' }"></div>
And the second one looks like this
<div data-bind="template: {name: 'myTemplate', data: $data, templateUrl: '/Templates/editable/' }"></div>
The problem is that when I get to the second template binding, knockout will reuse the first template since it has the same name , and that will make both templates readonly.
So are there any way to make knockout download the second template if it resides in another location that the other one, or are there no way around having unique names on all of the templates.
Given the current logic, it will be a lot of work to change that, so I'm hoping that it can be done through the binding instead of renaming.
EDIT
I'm using the External Template Engine found here.

It seems that as long as the name is unique it will fetch it again (even if that will result in the same url.
So changing them to
<div data-bind="template: {name: 'readonly/myTemplate', data: $data, templateUrl: '/Templates/' }"></div>
<div data-bind="template: {name: 'editable/myTemplate', data: $data, templateUrl: '/Templates/' }"></div>
Made it work as I hoped, with no changes on the server side :)
Thanks to #pax162 for putting me on the correct track!

Related

Multiple Views with nested views Angular

So first off, I'm working on this for a project at work, but none of us have any idea how to do it, so it might be kind of vague.
Here is the template of how it is going to look: Template
So View A & B are going to have 3 states in them that will change the content of the view based on which one is selected
The problem I'm having is that only 1 view ever shows up and it is a test template for now because I don't have those views built but none of the sub views of View A ever show up.
HTML
<div id="main">
<div ui-view="viewa" class="col-sm-7">
<!--Content of ViewA supposed to be here-->
</div>
<div ui-view="viewb" class="col-sm-5">
<!--Content of ViewB supposed to be here-->
</div>
</div>
States:
$stateProvider.state("main", {
url: "/main",
views: {
"viewa#": {
abstract: true,
template: "<div ui-view></div>"
},
"viewb#": {
templateUrl: "btemps/default.html"
}
}
}).state("bobtheView", {
parent: "viewa",
//This is default for viewa
url: "/",
templateUrl: "atemps/bob.html",
controller: "bobController"
}).state("billtheview", {
parent: "viewa",
url: "/bill",
templateUrl: "atemps/bill.html",
controller: "billController"
}).state("joetheview", {
parent: "viewa",
url: "/joe",
templateUrl: "atemps/joe.html",
controller: "joeController"
});
//Supposed to route to viewa showing bobtheview and viewb showing the template
$urlRouterProvider.otherwise("/main/");
So when I go to the page and go to the root it redirects to the otherwise but nothing shows up, upon just going to main, only the viewb template shows up.
Any ideas? Any way I can format it better too? Is it better to go with "viewa.bobtheview" over having the parent attribute in the mix?
UPDATE: So I found a work around, I loaded each of the bobtheview, joetheview and billtheview in html partials, then I refactored it so the view state of viewa and viewb are controlled within a main template that includes the "ng-include" function to load the different templates, and since all of the data that is stored in those views is given via JSON rest requests, there is no change in the data bindings. The problem I'm facing now, is updating that "ng-include" on button click, I haven't done extensive research on it but I plan on doing so and I'll report back when/if I find something. If you have any ideas on this let me know! :D.
So I found a viable answer to the question at hand, after extensive research and asking around, I went with the option of having 1 Controller and configuration state
$stateProvider.state("main", {
url: "/",
controller: "mainController",
templateUrl: "temps/primary.html"
});
$urlRouterProvider.otherwise("/");
That went into the configuration settings, then my controller looked a little like this:
app.controller("mainController", ["$scope", "$state", "$stateParams", "$http", function($scope, $state, $stateParams, $http) {
$scope.viewatemp = $stateParams.at; //Numeric value to represent template url for viewa
$scope.viewbtemp = $stateParams.bt; //Numeric value to represent template url for viewb
//Do some other stuff here
});
Then the HTML of "temps/primary.html" looked a little something like this:
<div ui-view="viewa" class="col-sm-5" ng-include="viewatemp"></div>
<div ui-view="viewb" class="col-sm-7" ng-include="viewbtemp"></div>
I did a little manipulation of the numeric value of viewatemp and viewbtemp to get the actual URL, those are being loaded from a JSON request from my ASP.net WebApi 2 Restful service, but all in all, it is quick, rather simple and still gets the job done and allows for further enlargement of the project.
And that there in solved my problem, cool thing about this, I can have as many as these as I want because they are all separate states with nested "views"
If you do have a better answer, let me know! This is only what I found and what worked for me.

dynamic directive: templateUrl

I really need help by solving the following problem:
I try to realize some settings for an application, therefore I want to use the UI-Bootstrap accordion.
I have the following HTML-Code:
<accordion close-others="oneAtATime">
<accordion-group ng-repeat="group in groups" heading="{{group.groupTitle}}">
<accordion-content></accordion-content>
</accordion-group>
</accordion>
The DOM of the "accordion" is a div where ng-controller="AccordionController". In this Controller I have a variable groups which looks like this:
$scope.groups = [{
groupTitle: "title1",
templateUrl: "file1.html"
}, {
groupTitle: "title2",
templateUrl: "file2.html"
}]; // ... and so on
accordionContent is my directive which should give different templateURLs depending on the $index or groupTitle (doesn't matter).
The accordionContent-directive looks like this:
settings.directive("accordionContent", function () {
return {
restrict: "E",
templateUrl: //**here is my problem**
};
});
The content also has some angular-stuff implemented, I read that this need to get considered. (or not ?)
I don't believe you can do that like that. I tried myself once, didn't work if I remember correctly.
What you can do is have a static HTML page in the directive, and in that HTML page you'll have:
<div>
<div class="slide-animate" ng-include="templateUrl"></div>
</div>
Where templateUrl is the variable on your isolated scope (or not isolated..) in the accordion-content directive.

KnockoutJS animated transition

Can anyone explain why the animated transition example in the knockoutjs website here uses the template binding? The example uses this:
<div data-bind='template: { foreach: planetsToShow,
beforeRemove: hidePlanetElement,
afterAdd: showPlanetElement }'>
<div data-bind='attr: { "class": "planet " + type }, text: name'> </div>
</div>
But the following works just as well:
<div data-bind='foreach: {data: planetsToShow,
beforeRemove: hidePlanetElement,
afterAdd: showPlanetElement}'>
<div data-bind='attr: { "class": "planet " + type }, text: name'> </div>
</div>
Here is their original fiddle. Here is my updated fiddle. The use of the template binding seems extraneous. Thoughts?
There is no special reason why the sample uses the template binding.
On the top of the page it is even mentioned:
When using the template/foreach binding, you can provide afterAdd and beforeRemove callbacks.
So it does not matter whether you use the foreach binding or the template binding in "foreach mode", because internally the foreach binding just delegates back to the template binding.
So they are essentially doing the same thing only with different syntax, however you have more options if you are directly using the template binding:
The main difference is that when using the foreach binding you cannot use named templates you have to use the inline template, but the in template binding you can specify any template even change it dynamically.

AngularJS - Run directives explicitly

I'm new to AngularJS and I'm struggling with the following issue.
I need to implement a 3 step workflow as follows:
Make a call to a web service that returns a list of strings. For example, ["apple", "banana", "orange"], etc. I intercept the response and add the angle brackets around each of these strings before I send it to the Views.
For each of the string returned by the service, I have to render
<apple />
<banana />
<orange />
Finally, get the actual AngularJS directive corresponding to each of those strings to "execute" (not sure what the right word is) and replace the elements above with the content from the templateUrl property as mentioned in each of their respective directives.
Right now, I'm doing Step 1 and Step 2 above using AngularJS. But I understand that they can be done using plain JavaScript using AJAX calls.
My problem is that the directives don't get "run" or "executed" and I have these tags displayed as plain text on the page -
<apple />
<banana />
<orange />
etc.
How do I tell Angular to replace the custom tags with the actual content from their templates?
Thanks for your help.
UPDATE: Here's what the code looks like:
<div class="content" ng-controller="mainController">
<ul class="feeds">
<li ng-repeat="fruit in fruits">
<div ng-controller="fruitSpecificController"> {{fruit}} </div> <!-- This renders <apple />, <banana />, etc. -->
</li>
</ul>
</div>
Also note that each fruit can have its own controller. In the code above, I say "fruitSpecificController", but ideally that would also be generated at runtime. For example, "appleController", "orangeController", etc. and yes, they'll be child controllers of the parent "mainController".
You can use the compile method, but there is a built in directive that will do this for you - if you are willing to load in via a URL.
ng-include
Using ng-include="'/path/to/template.html'" - the evaluated expression URL will be requested and added to the DOM as a child (compiled for you).
You can also cache the templates using $templateCache (if you want to request multiple templates at the same time or cache it for multiple includes).
That would look something like this:
$templateCache.put(/path/to/template.html, 'apple html string');
custom directive (with $compile)
Otherwise, if you want to load in and compile a string - use a directive inside of a ng-repeat.
.directive('unsafeHtmlCompile', function($compile){
return {
link: function(scope, element, attrs){
scope.$watch(attrs.unsafeHtmlCompile, function(val){
if(val !== undefined){
element.html('');
var el = angular.element(val);
element.append(html);
$compile(el)(scope);
}
});
}
}
}
Remember to remove the watcher, if your data won't change :-)
You probably just need to use the $compile service. The docs aren't super helpful but the gist is that you call $compile, passing in the DOM element (in your case the parent of your directives). That returns a function that you then execute, passing in the scope that you want to use ($rootscope is probably safe).
$compile(element)($rootScope);

AngularJS dynamically set param of ngInclude based on route

I'm trying to dynamically include a template into my index.html. The general structure of index.html is:
<body>
<header ng-controller="Main">
<section>
<!-- global stuff -->
</section>
<section ng-include="moduleName + '/views/menubar.html'">
<!-- module-based stuff -->
</section>
</header>
<div id="view" ng-view></div>
</body>
Sample URL
example.com/<app_name>/index.html#/<module_name>[/method_name]
I can't figure out how to update $scope.moduleName when the route changes. My trouble is two-fold:
The header's controller is Main, not the controller associated with the view, so I can't? update $scope.moduleName from the view's controller (because Main and the view's controller are siblings).
In Main, I tried setting a $scope.$on('$routeChangeSuccess',…), but apparently it is not notified of route changes.
I've thought of setting up a $rootScope.$on listener (as described in SO#15355346) for the route change and broadcasting down to children, who then emit back up their route, which is broadcasted back down so it is available to Main. But that seems heinous.
And I would really prefer to keep the header outside of ng-view.
EDIT I noticed that $route.current.scope has an object named with module_name (possibly because the name of the controller associated with the route's module_name is the same). I'm wondering if I might be able to somehow use the name of that object…
It's hard to say what's wrong in your code without the full picture. Things you show look fine to me.
Please see this plunk I've created to display the ability to do it. Take note that you also can extend route objects with custom properties, like moduleName here:
$routeProvider.when('/page1', {
template: 'one',
controller: 'one',
moduleName: 'firstModule'
});

Categories

Resources