angular-ui / bootstrap

PLEASE READ THE PROJECT STATUS BELOW. Native AngularJS (Angular) directives for Bootstrap. Smaller footprint (20kB gzipped), no 3rd party JS dependencies (jQuery, bootstrap JS) required. Please read the README.md file before submitting an issue!
http://angular-ui.github.io/bootstrap/
MIT License
14.27k stars 6.72k forks source link

make templateUrl configurable #743

Closed sym3tri closed 9 years ago

sym3tri commented 11 years ago

Now all templates must reside in the root directory under /template. This is not ideal for some larger projects.

pkozlowski-opensource commented 11 years ago

@sym3tri we've been discussing this in https://github.com/angular-ui/bootstrap/issues/105 but I'm not sure we are going to introduce configurability of paths.

What you should be doing really is to combine templates and put them in the $templateCache as we do in our distribution files that are bundled with templates, ex.: https://github.com/angular-ui/bootstrap/blob/gh-pages/ui-bootstrap-tpls-0.4.0.js#L2938

What is your use case? Are you customizing all the orginal Bootstrap templates?

sym3tri commented 11 years ago

My use case is this:

I would like the option to customize the Bootstrap templates.

I will pre-compile all the templates for production releases.

However while I am developing locally I would like to choose where I put the templates and not have to recompile them each time to view changes.

pkozlowski-opensource commented 11 years ago

@sym3tri OK, your use-case is the only legitimate one I could think of :-)

So, we've got a couple of ideas for technical solutions but all of them are pretty bad, I'm afraid. The only reasonable way of dealing with it would be through a constant. But it would mean that the same constant needs to be repeated in each and every module :-( Need to think about some more...

What I would suggest for now is to use the <script> tag (http://docs.angularjs.org/api/ng.directive:script) in your HTML to develop customized templates as described here: http://stackoverflow.com/a/17677437/1418796

sym3tri commented 11 years ago

@pkozlowski-opensource Thank you so much. I didn't think of overriding the templates like that. Seems like a good approach.

pkozlowski-opensource commented 11 years ago

@sym3tri Glad you've got something workable for now. I'm going to close this one for now and give this issue a bit more thought when looking into #105

scarletsky commented 11 years ago

@pkozlowski-opensource I think i should ask my question in this issue. My ui-boostrap template path is views/ui-bootstrap-tpls/alert.html

<script id="template/alert/alert.html" type="text/ng-template">
    <div ng-include=" 'views/ui-bootstrap-tpls/alert.html' "></div>
</script>

Is it means that I should write these code in my own template?

In views/ui-boostrap-tpls/alert.html

<div class='alert' ng-class='type && "alert-" + type'>
    <button ng-show='closeable' type='button' class='close' ng-click='close()'>test</button>
    <div ng-transclude></div>
</div>

And my own views

<div ng-controller="AlertDemoCtrl">
    <div alert ng-repeat="alert in alerts" type="alert.type" close="closeAlert($index)">
        {{alert.msg}}
    </div>
    <button class='btn' ng-click="addAlert()">Add Alert</button>
</div>

I've try these but it seems not work. Although I can see the 'test' button, but there is no alert.msg. Is there anything wrong in my code? I really want to know how to config the templateUrl in ui-bootstrap.

pkozlowski-opensource commented 11 years ago

@scarletsky the whole point of this discussion is that IMO templateUrl should not be configurable as there is no point of downloading templates from the web server. I'm not sure what is your use case but if you want to override one of the default templates please use the technique described here: http://stackoverflow.com/a/17677437/1418796

If you are fine with default Bootstrap templates and you don't want to override anything just make sure that you are including proper file (the one with templates bundled). If in doubt just include ui-bootstrap-tpls-0.6.0.js. You only need one file to include, more info here: https://github.com/angular-ui/bootstrap/tree/gh-pages#build-files

If you've got another use-case not covered here please provide more info.

scarletsky commented 11 years ago

@pkozlowski-opensource Yes, I just want to override the default templates and put my own templates in views/ui-bootstrap-tpls/. And I've tried to use this, but it do not work.

<script id="template/alert/alert.html" type="text/ng-template">
    <div ng-include=" 'views/ui-bootstrap-tpls/alert.html' "></div>
</script>
joshdmiller commented 11 years ago

@scarletsky Using ngInclude like that probably won't work in most circumstances; many of the components require knowledge of the dom structure inside the elements, which has now been changed by the use of the ngInclude DIV. Further, ngInclude fetches asynchronously, meaning that the contents of it may not be available to the directive anyway at the time it initializes or fires.

If you want to keep your directives in an independent folder structure, you'll need to compile them yourself into the template cache at the paths/IDs of the default templates. Otherwise, the templates must be placed inline within the script tag and not with an ngInclude.

scarletsky commented 11 years ago

@joshdmiller Is there any example ? I don't want to put my own template in the script tag.

joshdmiller commented 11 years ago

@scarletsky You only have three options: (a) put your templates in the $templateCache at the appropriate paths (recommended); (b) put your templates in a script tag; or (c) monkey-patch the bootstrap files with a modified template path (not recommended).

There is a PR that addresses customizing the template paths (#1088), but I personally don't see a strong enough use case to warrant it. It is the opinion of this project that templates should be put into the $templateCache; you're encountering this issue because you didn't and then changed the paths.

scarletsky commented 11 years ago

@joshdmiller You mean I should use

$templateCache.put('template/alert/alert.html',   
    '<div>' +
        ' /* My own template */ ' +
    '</div>');

and there is no need to add a folder for my own ui-boootstrap-templates ?

joshdmiller commented 11 years ago

@scarletsky That would work fine, but I actually meant as part of a build. If you have an existing, Grunt-based build process you can use the grunt-html2js plugin to automatically do it for you (though you'll need to change a few things to map paths correctly).

As part of a build, you'd want to read in your template files and then add them to the $templateCache, concatenating the resulting file with the rest of your app sources. E.g. you'd read in the file views/ui-bootstrap-tpls/alert.html and add to the $templateCache as template/alert/alert.html. Check out the build process for this project to see how that works in general: https://github.com/angular-ui/bootstrap/blob/master/Gruntfile.js#L91. I am not aware of any example that uses custom paths to which I count point you, but the general approach is very common.

If you have no build process, then this option is not available to you; but without any build process, you also wouldn't have concatenation or minification or any such goodness, so I would just recommend to use one (e.g. Yeoman or ngBoilerplate or a custom one).

scarletsky commented 11 years ago

@joshdmiller It seems that grunt-html2js can solve my problem in a elegant way. Thanks.

wesleycho commented 11 years ago

I should mention, another way to override the template location is to use $provide.decorator to override the templateUrl property of the directive.

ajoslin commented 11 years ago

Nice Wesley! That should be in the README.

myApp.config(function($provide) {
  $provide.decorator('datepickerDirective', function($delegate) {
    //array of datepicker directives
    $delegate[0].templateUrl = "my/datepicker.html";
    return $delegate;
  });
});
tom10271 commented 9 years ago

This hack does not always work. For tooltip you cannot set custom template to dedicated URL.

For tooltip it first go to grab: http://store.wip.dev/bundles/warehouse/scripts/src/DEV/UIUXModule/directives/templates/tooltip/tooltip-popup.html

But then it goes back to its default URL: http://store.wip.dev/administration/warehouse/template/tooltip/tooltip-popup.html

while http://store.wip.dev/administration/warehouse/ is the URL to my application. Obviously Angular UI is going to get a template by my application's relative path.

I use require.js to construct and boot Angular application. Files are putting in different folders.

Angular UI NEVER able to grab the correct URL. It forces me to develop my own directive. It is good to provides UI component to developers but it is working yet not quite working great at all I am afraid.

aariacarterweir commented 9 years ago

How do you not see a strong enough use case? Don't you develop locally?

wesleycho commented 9 years ago

You can already specify a custom template url for each component that uses a template currently in UI Bootstrap on an instance basis.

aariacarterweir commented 9 years ago

I've installed via bower. I do not want to put the templates in my root directory as that makes absolutely no sense. I am developing locally and do not wish to compile every time i make a change.

Basically I'm a regular user IMO - and you don't cater to my use case at all. It absolutely stupefies me that you have issue with storing the base template path as a variable somewhere.

Is there anything I can do to set the base template path or do i need to set it per directive?

wesleycho commented 9 years ago

You do not need to put the templates anywhere - they are available in memory via $templateCache when consumed via the angular-bootstrap bower package.

Basically I'm a regular user - and you don't cater to me.

This is a poor attitude & highly disingenuous given that most developers have been able to work around this with minimal overhead - this attitude has no place in open source software or software development of any kind.

pkozlowski-opensource commented 9 years ago

@aariacarterweir I've got an impression that you are misunderstanding the idea behind the open source software: it is not a free software delivery service where you can demand any feature and have it done for free. It is a collaborative effort where anyone is free to contribute code via PR if one sees area of improvement.

Having said the above I don't think there is anything to "fix" here as we do distribute scripts with embeded templates, see the full details here: https://github.com/angular-ui/bootstrap/wiki/FAQ#im-using-modal-service-but-it-cant-find-its-templates

aariacarterweir commented 9 years ago

@pkozlowski-opensource I've got the impression that you're completely disconnected from the real world and over-burdened with the needless and illogical complexity of angular in general.

I don't expect to be able to demand features. I do however expect anyone who has influence on this project to try and listen to their users. You just come across as a defensive and unreasonable.

The templateUrl should be configurable. It absolutely blows my mind that you cannot see the reason for this. "I know a really complex way of getting around it so it doesn't need fixed" is not exactly a progressive response. I could submit a PR with changes made to implement this, but you wouldn't merge it - so what's the point?

Thus I'm on here pleading with you and the other key devs in general, to please reconsider this issue.

BobbieBarker commented 9 years ago

Hey you're attitude isn't welcome here, your derogatory commentary doesn't reflect the spirit of discussion that we aspire towards in this community. Please refrain from further posts like this and if you don't like the way the library works you're more than welcome to fork it and make it work how you want.

wesleycho commented 9 years ago

@aariacarterweir I think there is a misunderstanding - this issue has been reconsidered in the past couple of months and I implemented configurable templateUrl for every component in UI Bootstrap myself. One can see the current documentation on the project homepage to see that it it is present on every component.

Please refrain from childish attacks on @pkozlowski-opensource though - he did nothing to deserve that, especially considering that most developers in open source work on the software in their spare time. He created this project in his spare time as a service to the developer community, there is no reason for this level of disrespect - this type of behavior drives developers away, and creates a negative atmosphere. His company consumes the library, so he is a real world user of the library as well.

BobbieBarker commented 9 years ago

Apparently my peers don't feel like I should of locked the thread. I don't want to see a flame war in the issues section on this repo though. Please, refrain from further negative commentary or I will start locking threads.

ProLoser commented 9 years ago

Okay guys. Everyone take a :sunglasses: :pill:

aariacarterweir commented 9 years ago

I hope you can understand why I was a bit ruffled by the wording of @pkozlowski-opensource's reply to me. I honestly felt like it was conceited and snotty. Criticizing me for my reply and defending his seems a little one-sided and overly sensitive. My suggestion is that the decision to make the templateUrl base path not simply configurable be explained in clear language. There are clearly users out there who would benefit from this feature and judging by the number of questions posed about this issue I'm far from alone.

I didn't mean to come across as derogatory and I apologize for any offence caused.

ProLoser commented 9 years ago

@aariacarterweir I've actually been a bit frustrated with having to override templates via the cache too. I started exploring avoiding templates almost completely in ui-mention and discussions around rewriting ui-select. In ui-select we explored providing a configurable theme which would adjust what templateUrl was used, but none of these solutions were terribly wonderful.

aariacarterweir commented 9 years ago

@ProLoser I agree. For me the issue is that '/templates' is hard-coded. I don't believe this should be the case and nobody has made a solid argument for it as far as I can see. People criticizing me for my 'attitude' and declaring me unfit for software development of any kind is a bit OTT!

When we understand something it becomes easy. When we find ways around problems then the problems sort of cease to exist for us. But for newer users, a high barrier to entry begins to build up sometimes without us realizing. angular-bootstrap is a great resource, by contributing on this issue thread I thought I was furthering robust discussion around something I deemed important. I think I have a pretty good attitude and I'm happy to admit when I'm wrong.

wesleycho commented 9 years ago

People criticizing me for my 'attitude' and declaring me unfit for software development of any kind is a bit OTT!

Let me be clear before moving to technical discussion since this statement seems to suggest that the earlier comments in this issue are acceptable (even ignoring the fact that nobody said anything about being unfit for software development) - the Angular team itself has made clear that they will actively work to marginalize anyone who does not act with respect towards others, and I am in full agreement & enforce it. Let me lay out the offending comments that were said without any prior instigation.

Basically I'm a regular user - and you don't cater to me.

@pkozlowski-opensource I've got the impression that you're completely disconnected from the real world and over-burdened with the needless and illogical complexity of angular in general.

These are highly offensive, offtopic, & clearly meant to escalate/incite/harm and nothing that has been said that warranted any response on this level - Pawel did not mean to offend, and his conduct throughout his open source contributions illustrates this vastly. I also take offense since after being involved in development for the past half year, it hurts to have it suggested that I don't care about how people use this library. Pawel's company (not Google) and my company are both consumers of this library, and my original involvement with UI Bootstrap starting a half year ago was not for work, but purely for wanting to help people with pain points in using a popular library that saw minimal development work for more than half a year prior. This type of non-professional emotional outburst helps drives developers to give up maintaining open source libraries - such incidents in the past have made me reconsider my own involvement at times, as it is extra undeserved abuse that I don't need to put up with.

If there is a belief that any of the maintainers misunderstand the request, simply lay it out in detail - we help maintain this project for the community, thus we have no problem with (re-)considering any request that has merit. We need details though as to why x situation does not work, especially given that there are ways of overriding templates and that we already support instance level overriding of templateUrl - we are not mind readers unfortunately (although it would be awesome if we were, it would solve a lot of problems :grinning: ).

Now, to explain the history & situation - the use of a hardcoded url is simply because the templates need to live in a location due to how Angular works. This is the origin of the hardcoded template/component/templateHtml.html url came about. The templates are currently bundled in $templateCache to be available in memory for performance reasons of avoiding a roundtrip to the server to fetch templates, to serve as a sensible default for most users, and to serve as an example of a recommended best practice for most apps to pre-build templates into $templateCache.

Previously, Angular did not provide great flexibility to modify templateUrl dynamically - this was not added until Angular 1.1.4 if I recall correctly, and this library predates that. This originally would have been difficult to accommodate different use cases. With the allowance of functions with templateUrl, we can allow configuring of the url at minimal cost. There have been other issues where the use case has been made clear that this would be a useful feature, and so within the past couple of months, I implemented instance level overriding of the templateUrl string used - here is one documented example.

Correct me if I am wrong, but the one missing configuration feature is a global setting of the default templateUrl string, even though it can be overridden on an instance basis. There are several approaches we much consider here, and this is where it gets murky as to whether there are good technical solutions here.

1) A global configuration object where users can override all templateUrls for each component that every component is dependent upon 2) Configuration injectables for each component 3) A global configuration object where users can modify the base templateUrl and keep the rest

Example of 1

angular.module('ui.bootstrap.config', [])
.constant('uibConfig', {
  alert: {
    templateUrl: 'foo/bar.html'
  },
  dropdown: {
    templateUrl: 'baz/boo.html'
  },
  ...
});

Example of 2

angular.module('ui.bootstrap.dropdown', [])
.constant('uibDropdownConfig', {
  templateUrl: 'foo/bar.html'
});

angular.module('ui.bootstrap.pagination', [])
.constant('uibPaginationConfig', {
  templateUrl: 'baz/boo.html'
});

...

Example of 3

angular.module('ui.bootstrap.config', [])
.constant('uibConfig', {
  baseTemplateUrl: 'foo/'
});

All three approaches have major usage issues. The first two allow maximum flexibility, but the first has a problem of potentially unnecessary configurations polluting modules users are consuming (and adds to code bloat), and the second puts the user in a mess inside a configuration block. The problem with the third example is that it is not flexible enough.

I do not see a good solution here that does not result in pain, and while it may be tempting to go with any solution that accomplishes a desired goal, the problem is that the UI Bootstrap team will be left with the maintenance burden of poor choices, so we must consider requests carefully.

I am open to other suggestions too, but it must meet the criteria of good API design, accomplishing maximal ease of use, and avoiding unnecessary breaking changes. All three options I laid out I am seeing currently fails the first criteria, and in the third example, the second criteria. I don't mind necessarily settling, but it requires thought.

aariacarterweir commented 9 years ago

@wesleycho Right okay, I take your point.

I disagree with this:

These are highly offensive, offtopic, & clearly meant to escalate/incite/harm and nothing that has been said that warranted any response on this level

I think you're being overly sensitive. I accept that my response to @pkozlowski-opensource's reply was out of line:

@pkozlowski-opensource I've got the impression that you're completely disconnected from the real world and over-burdened with the needless and illogical complexity of angular in general. I should have bitten my tongue, but I did not as I had interpreted his reply as being quite impolite at first.

You've made your point very clearly @wesleycho, you've accused me of being:

In some cases I think your assessment of my behavior is fair. I was frustrated when I wrote my original comment. However I'd also like to point out that you've reacted with plenty of emotion of your own.

Sometimes tone & nuance is lost over the internet. Sometimes I let my emotions run a little too hot too.

So I'd like to sincerely apologize for any offence I've caused. Despite how I feel about angular, it's great work you all do here and you should know that I do appreciate it. So from me to you, thank you, sorry, here's an olive branch - and keep up the great work.

<3

wesleycho commented 9 years ago

I stand by all my comments in the context of the original discussion - they are merely labels of the the behavior exhibited prior. They are not ad hominem as they are descriptions of the behavior. Not once did I say things that don't follow from the behavior such as "you're a terrible person" or "you're a liar" - I held my tongue.

The poor attitude part was that the aforementioned comments were counterproductive towards coming towards accomplishing the end goal of trying to figure out a way to try to meet your needs - I ended up writing an essay instead of writing code :wink: , and all people experienced emotional blowback instead before we moved this towards trying to resolve the issue.

It looks like we all calmed down some, and I accept your apology, as well as apologize if my characterizations caused you distress.

Do you have thoughts on my assessment of options for finding a way to globally configure the templateUrl? I would still like to try to choose an approach to get this in the library to help simplify people's code.

aariacarterweir commented 9 years ago

Thanks for accepting my apology @wesleycho, I really don't want to cause offence.

I do not see a good solution here that does not result in pain I agree now that I understand the matter more clearly.

Obviously on build, everything will go into $templateCache which also makes sense. However during local dev when the developer does not wish to build to test changes I see the value in having something like option 2, with a 'fallback' of option 3.

This meaning that option 2's custom template takes precedence over the base path set in option 3. Right now there is a way (as you pointed out before - and indeed I think you developed this functionality) to change the template per directive so maybe that can stand in for option 2.

From a user-end perspective this makes sense to me. There may be use cases where this doesn't make sense however and it may not even be possible at all.

BobbieBarker commented 9 years ago

"However during local dev when the developer does not wish to build to test changes"

If you're using proper tooling isn't that a null point?

Foxandxss commented 9 years ago

I highly recommend you @aariacarterweir to cache your templates even in development. There are some race conditions between template loading and directive initialization that can't be really avoided (even when I don't think we have this issue here).

So it is highly recommended by the angular team that you always cache your templates when working with them.

It shouldn't be a problem with proper tooling. You can have a task (gulp, but include others) that looks for every template in your app and cache them into $templateCache automatically. That works good for dev and prod. Look for gulp-angular-templatecache for example.

ProLoser commented 9 years ago

@Foxandxss race conditions from templates sounds like poor directive design :/

Foxandxss commented 9 years ago

@ProLoser Angular team doesn't have the same opinion.

pkozlowski-opensource commented 9 years ago

@ProLoser what @Foxandxss was saying is that in ng2 compiler sync and async compilation goes through 2 different paths and you could, in theory, run into different issues (we add the time component here, as soon as we fetch things from the server). But I'm not sure if $templateCache changes much here, as we still go through the asyn path

Foxandxss commented 9 years ago

To be concrete. I have a directive which I initialize manually (makes sense that way), there is no human way to wait until a template is loaded so I can trigger the initialization. The official response to that, cache all your templates beforehand.

SoundLogic commented 9 years ago

you could just use this helper to pass a template to template rather than using templateUrl (I think most places that have templateUrl also have template options?):

(function () {
    'use strict';

    angular
        .module('app')
        .factory('templateHelpers', templateHelpers);

    templateHelpers.$inject = ['$http', '$templateCache'];

    function templateHelpers($http, $templateCache) {
        var service = {
            getTemplatePromise: getTemplatePromise
        };

        return service;

        function getTemplatePromise(templateUrl) {
            return $http.get(angular.isFunction(templateUrl) ? (templateUrl)() : templateUrl, { cache: $templateCache }).then(function (result) { return result.data; });
        }
    }
})();