I am building an angular app, with templates changing depending on with ngscope as seen on the samples below.
<label class="btn btn-default">
<input id="cacher" value="settings" type="radio" data-ng-model="user.status">Settings
</input>
</label>
and here i'm calling my html template userPageSettings
<div id="wrapper" ng-switch="user.status">
<div ng-switch-when="settings">
<user-page-settings></user-page-settings>
</div>
</div>
Trouble is, i'm trying to get access to the templates (and the correct route) through a dropdown in the navbar, which is supposed to do exactly the same thing as my normal menu. But if i simply call the templates from my navbar they will be shown on the current page, therefore at the incorrect route.
In my navbar code i'm doing the following, which is bringing me back to the correct route, but showing me my default template (aka home, not shown here, but called through the same method and has the number 6)
<li role="menuitem">Settings</li>
The number "1" (and the others using 2,3 etc) are hashed in my javascript and put into a variable. I would like to make my templates appear depending on those numbers.
Any ideas how ??
:) Thank you !!
Related
I'm following this Ember tutorial and I've suddenly run into an issue where my rental-listing.hbs template component stops rendering. It started when I began implementing the map service.
I don't understand where this could be happening. I've copied the code from parts of the GitHub repository that I thought were relevant but to no avail.
I have a rental.hbs template that looks like so:
<div class="jumbo">
<div class="right tomster"></div>
<h2>Welcome!</h2>
<p>We hope you find exactly what you're looking for in a place to stay.</p>
{{#link-to "about" class="button"}}
About Us
{{/link-to}}
</div>
{{outlet}}
Which in turn has a template component called rental-listing.hbs:
<article class="listing">
<a
onclick={{action "toggleImageSize"}}
class="image {{if this.isWide "wide"}}"
role="button"
>
<img src={{this.rental.image}} alt="">
<small>View Larger</small>
</a>
<div class="details">
<h3>{{link-to this.rental.title "rentals.show" this.rental class=this.rental.id}}</h3>
<div class="detail owner">
<span>Owner:</span> {{this.rental.owner}}
</div>
<div class="detail type">
<span>Type:</span> {{rental-property-type this.rental.category}} - {{this.rental.category}}
</div>
<div class="detail location">
<span>Location:</span> {{this.rental.city}}
</div>
<div class="detail bedrooms">
<span>Number of bedrooms:</span> {{this.rental.bedrooms}}
</div>
</div>
<LocationMap #location={{this.rental.city}}/>
</article>
The only thing I have added to the above is the line <LocationMap #location={{this.rental.city}}/> but it still doesn't work if I remove it.
The console shows me no errors and I can actually see I am getting the three dummy objects I want from Mirage:
So I'm definitely getting the objects and from what I see I'm doing everything necessary in the templates to render it but they aren't. Should I be looking somewhere else?
Are you able to provide a link to your example? By having each piece of the ember application you mention it would be best to answer definitely. I can give a general answer with strategies for debugging the template.
The conventions behind ember.js make understanding the "whys" frustrating at first and possibly opaque. Ember's handlebars implementation governs how values are populated and accessed within templates using very specific rules. Ember treats templates (handlebars files) differently depending on whether it is for a route or component. Component's have an isolated context and must receive values by explicit passing in or dependency injection. Then, you can use such values in a component's template by accessing those properties with {{this.somePassedInValue}}.
In the super-rentals app, it appears the rental index route invokes the components responsible for displaying the individual units. I found this in app/templates/rentals/index.hbs.
<li><RentalListing #rental={{rentalUnit}} /></li>
The route template iterates over the list of filteredResults. Each of these is the rentalUnit. A good first step would be to use the {{log}} helper to print out that the value of rentalUnit to ensure it is what you expect.
Alternatively, you could try cloning https://github.com/ember-learn/super-rentals and applying the changes you want to make step by step from the master branch. By doing so, you could easily undo a single change to see what caused something to not show up as expected.
<LocationMap #location={{this.rental.city}}/>
to be written as below
<LocationMap #location={{this.rentals.city}}/>
may be typo error.
also repeat this for there place in that template.
Because the model name in the console is rentals not rental
I am using oi-select library, i have customized it according to my project need and i have written directive for it. The problem is its taking too much of time say 10secs to load around 3k array data. I want to minimize the time for it. Here I have created plunker for this.
I have directive which loads all factory data and provides it to oi-select, in my code here is html
<small><b>select {{MPhardwaresListDropDown.length}}</b></small>
<div style="padding-top: 7px">
<div title="" class="selected-multiple-items">
{{MPselectedHardwares.length}} selected
</div>
<grid-multi-select id="hardwareId" clean="clean" optionlist="MPhardwaresListDropDown" colval="name"></grid-multi-select>
</div>
HTML code in directive looks like
<div>
<div ng-repeat="optionVal in tempOptionList | orderBy : ['-originalcheck','+label']" prevent-close>
<div ng-if="optionVal.label">
<label class="checkbox" ng-if="!typeFilterOptions || (optionVal.label.toString().toLowerCase().indexOf(typeFilterOptions.toLowerCase()) > -1)">
<input type="checkbox" name="checkbox1" ng-checked="optionVal.check" ng-model="optionVal.check"/>
<span class="checkbox__input"></span>
<span class="checkbox__label" style="color:#A9A9A9;">{{optionVal.label}}</span>
</label>
</div>
<div ng-if="!optionVal.label">
<label class="checkbox" ng-if="!typeFilterOptions || (optionVal.val.toString().toLowerCase().indexOf(typeFilterOptions.toLowerCase()) > -1)">
<input type="checkbox" name="checkbox1" ng-checked="optionVal.check" ng-model="optionVal.check" ng-change="checking(typeFilterOptions)"/>
<span class="checkbox__input"></span>
<span class="checkbox__label" style="color:#A9A9A9;">{{optionVal.val}}</span>
</label>
</div>
</div>
angular code is too big to mention in this question please refer plunker, but this is how it loops
scope.selection = scope.selectionorg;
scope.checkedall=scope.checkedallorg;
scope.OptionList=parentScope[scope.parentListName].sort();
scope.tempOptionList=[];
var i=0;
for(i=0;i<scope.OptionList.length;i++) {
scope.tempOptionList[i] = {val: scope.OptionList[i], check: false, originalcheck: false};
}
if(scope.optionListSelectedList.length>0) {
angular.forEach(scope.optionListSelectedList, function(obj){
angular.forEach(scope.tempOptionList, function(obj1){
if(obj===obj1.val){
obj1.check=true;
obj1.originalcheck=true;
}
});
});
}
else{
scope.checkedall=false;
}
};
I want something like which will load partial data on scroll it loads more data, any help will be appreciated. Thank you so much.
EDIT
Now i have edited my plunker with limitTo in ng-repeat, for that i have written new directive which will trigger addmoreitems function when scroll will reach bottom. updatedPlunker
Now problem is when i am typing and searching something its searching in only available records with respect to limitTo its not searching in all data, say now the limitTo is 50 then search is happening only in 50 records not in 3k records.
Correct way of doing this kind of requirement is to do with pagination, since you are loading data from the server side, you should make your api to support pagination.
It should accept three parameters such as number of items, start, limit and you should initially get the number of items and repeat it until you get the whole result set.
There should be another request to get the total number of items. By doing this you can retrieve all the elements at once loaded in the client side. Until that you should have a loading indicator, or you could load this data when the login process/application starts.
limitTo will not be able to help with the search , because you are limiting the results.
The problem is not the array, the browser can easly handle that, the problem is that you're rendering all the 3k DOM elements, that's really heavy work even for an actual machine, also since there is bindings in each dom element {{}} they're being watching by AngularJs, I got the same problem and solved using Virtual Repeat from AngularJS Material, what this does is it doesn't render the whole 3k DOM elements generated by the ng-repeat, instead it just renders the ones that are visible, also I've found another library if you don't want to use Angular Material, this seems to work the same way: Angular VS-Repeat
You may try the limitTo filter in ng-repeat in angularjs which takes the additional argument to start the iteration.
https://docs.angularjs.org/api/ng/filter/limitTo
On scroll, you can then change that argument based on the number of items pending or left for rendering or the number of items already rendered. This should help you in approach of selective loading of data on scroll.
On Durandal, I have a login page that's styled different from my other pages, so taking the advice I saw on some of the posts here, I've set up main.js to do a:
app.setRoot('viewmodels/login');
And on the login page, I provide the below method in login.js that resets the root:
loginRedirect: function() {
app.setRoot('viewmodels/shell');
}
The idea is that the user should go to the login page by default, and once logged in, I will invoke the method loginRedirect, which sets shell.html to be the root and thus reloads the content, and he should be able to navigate other pages from there.
Here's my login.html:
<div class="login">
<div data-bind="compose:'header-nav-plain.html'"></div>
<div class="container">
<!-- Content here-->
</div>
</div>
<div data-bind="compose:'viewmodels/footer'"></div>
<a class="go-inner-pages" data-bind="click: loginRedirect" href="#">Test link to go to 'inner' pages</a>
And here's my shell.html, that is the doorway to all other pages:
<div>
<div data-bind="compose:'viewmodels/header-nav'"></div>
<div class="container">
<div data-bind="compose:'viewmodels/site-nav'"></div>
<div data-bind="router"></div>
</div>
<div data-bind="compose:'viewmodels/footer'"></div>
</div>
You can see that shell.html is slightly different in structure from login.html, in that it binds to a different header-nav.html, and also a site-nav.html. (They share the same footer.html.) So on the login page, when I click on the test link that calls the loginRedirect method, it sets the root of the app to be shell.html, which will load the default page based on the parameters passed to router.map, as defined in shell.js:
activate: function(){
//Initializing router
router.map([
{ route: '', moduleId: 'viewmodels/dashboard', title: 'Dashboard', nav: true }
])
.buildNavigationModel();
//More code
return router.activate();
But, when I click on the test link and invoke loginRedirect, the new dashboard content is loaded, but the new header-nav and site-nav data binding didn't happen. I checked the inspector, and see that the structure has indeed changed to that of shell.html (as opposed to login.html), only data binding for header-nav and site-nav didn't happen. I.e. here's the new html after invoking loginRedirect:
<div>
<div data-bind="compose:'viewmodels/header-nav'"></div><!--header nav content not bound-->
<div class="container">
<div data-bind="compose:'viewmodels/site-nav'"></div><!--site nav content not bound-->
<div data-bind="router"><div class="durandal-wrapper" data-view="views/dashboard" data-active-view="true">
<!-- Dashboard content successfully loaded -->
</div><!--end data-bind="router"-->
</div><!--end .container-->
<div data-bind="compose:'viewmodels/footer'">
<!--footer content loaded-->
</div>
And from the Console (see below), one can see that while the shell and dashboard are bound, (even footer is bound a second time), header-nav and site-nav are not.
Does anyone have any idea why this is happening and what I might be doing wrong? Thanks in advance for your attention, hope to hear from someone soon. Cheers.
For view-only bindings, you would bind this way (we do the same as you, so I'm copying and pasting our code):
compose: {view: 'shellLeftFooter.html'}"
For views bound to viewModels, you bind this way:
compose: {model: 'viewmodels/navigation/shellNavigation'}
The default viewLocator will use the following convention:
If a view is specified with no model, then the view must be identified with the .html extension, as I have done in the former example, and it will not be bound to a viewModel;
If a model is specified with no view, as I have done in the latter example, then the model file is assumed to have a .js extension, and it is also assumed that a view exists in a folder named views under the app directory, with the same name as the model, but with an .html extension.
These aren't the only two conventions, but they are the ones germane to this discussion. To put the above in different words, make sure you have both a viewmodels and a views directory that are siblings of each other under the app directory. Make sure you give the viewModel and the view identical names, differing only in their extensions (.js for the former and .html for the latter).
I'm seeing all of your .html and .js files in the code you posted, so I'm sure what your intention is in each case (view only or model-view-viewModel). Also, your compose binding is not quite to standard.
I'm building SPA application with knockout.js .
Basically what i do is bind as current page a ko component depending on what is route.
It looks something like
<div id="currentPage" data-bind="component: { name: currentRoute.page,
attr: currentRoute }>
</div>
This is for the current page and the whole picture whit the layout looks like:
<html>
<head></head>
<body>
<div data-bind="component: {name: "nav"}></div>
<div data-bind="component: {name: "aside"}></div>
<div id="currentPage" data-bind="component: { name: currentRoute.page,
attr: currentRoute}">
</div>
</body
</html>
The problem is that i dont have one layout.. the other one looks like this (pseudo used)
<html>
<body>
<wrapper>
<currentPage>
</wrapper>
</body
</html>
So basically the first layout is not direct parent of the currentPage module but the second is..
The variants for dynamic layout changing that i can think of are
Specify the layout components in each page.. But i dont think it is good idea cause i will be writing the same code over and over and will not be able to persist the state in the layout component because of when i change the next page the layout will be recreated( not fully but enough to loose the state )
The layout is separate from the currentPage component so only the currentComponent binding will be changed.. this is perfect for persisting state in layout but not good when i have variant where i want different layout for example which is wrapper around the componentBinding..
I'll be very happy if some one shares fresh ideas how to solve such problems.
In knockout if any part of view is to be dynamically changed you got to have bindings attached to the view. You say that the outer layout of the page is dependent on the inner page being displayed, so logically I consider it part of that page view definition. That is why I would not look for some special way to handle this case.
If I am not missing something, the reasonable thing to do in this case is to just include layout in each page. You could then handle repetitive code by using "traditional" approach using knockout templates (especially Note 5 on dynamic templates from http://knockoutjs.com/documentation/template-binding.html) and code extraction.
Completely other approach you might want to take is to put all layouts in master page and then control them by using bindings like "if" and "visible".
I will start by explaining what we are trying to achieve.
We have just jumped on the Angular bandwagon and are building a prototype with it to see what it is capable of.
Currently there is a load of data (grades in this case) on the server and we display that data using Angular.
The following is the code we use:
<ul class="phones">
<li class="list-group-item" onclick="toggleExpandedView(this, true, 500)" ng-repeat="grade in grades | filter:query | orderBy:orderProp">
<span ng-show="grade.comment"><img src="../Content/images/comment.gif"/></span>
<a class="btn btn-primary editButton" style="float: right; position: absolute; right:10px;" ng-href="#/grades/{{grade.gradeId}}">Edit</a>
<div class="heading1"><a ng-href="{{grade.url}}" target="_blank">{{grade.gradeValue}}</a></div>
<div>Provided by {{grade.assessorFirstname}} {{grade.assessorLastname}} on {{grade.dateModifiedFormatted}} </div>
<div class="expandedGrade" onclick="childDivClick(event)" style="display: none" id="grade-{{grade.gradeId}}">
<label>Attachments</label>{{grade.attachmentCount}}
<br />
<span ng-hide="editing" ng-click="editing = true"><b>{{grade.comment || 'No comments yet'}}</b></span>
<form ng-show="editing" ng-submit="editing = false">
<input type="text" ng-model="grade.comment" placeholder="Comment" ng-required />
<br />
<input id="saveChanges" type="submit" class="btn btn-primary" ng-click="saveChanges(this, grade)" text="Save changes" />
</form>
</div>
</li>
</ul>
As you can see we have a parent ul and for each grade in grades we simply display a li and a div that is hidden and when you click on the li we use jQuery to animate and display the hidden div. In the child div we have a comments field which users can update and click save. On save the current object gets fired back to the server however we are concerned about the fact that Angular has to go through all 2000 grades until it finds the one we are updating (due to two way binding) and this means that everything will be really slow and we cannot afford that.
This is what we want to achieve:
1 We want to bind the data one way so that we can display the list of all grades on the screen and as soon as they are all displayed we want to somehow remove the bindings.
2. When users update the comments for a particular grade we then want to dynamically bind that particular grade so that Angular knows exactly which one it has to update without having to go through the whole collection of 2000+ grades.
I have find a tutorial however I am still unsure how to integrate that into my code.
I have also watched this video and I understand the concept behind it but again, I am struggling to write something that actually works ( we have just started using Angular so I am pretty much a newbie)
Could anyone out there point me in the right direction and provide me with some code samples that would solve the issue we are facing? Any advice and help would be greatly appreciated
You could always use a directive
The logic should go like this
use a service to hold your grades
inject the service into your directive
make a copy and just bind the 'read only view' in your directive
you could watch the service for changes and makes updates as needed
in your directive
as far a lazy loading / updating when needed - use a data service
and call the data service for an update whenever the trigger fires
if your trigger needs to come as a push from some other 'web service' consider a technology
like http://socket.io/
I can put together an example if you would like to see how the services and directives should interact