I have a widget which comprises of a side bar and the content area. The sidebar has a tree component with several tree items. When a tree item is clicked,it fetches its own data and injects the data into the content div.
I am using ajax get to fetch the data from the server and inserting into the content.
While going through angular,i found this method http which makes http requests to a specified server.
In my data widget,i am using jquery and html5 and no angular.The widget works but i am curious how angular js would have approached the building of the same widget.
What are some concepts from angular js that i could have used to come up with the same widget?.
Here's a real basic concept of how I would create a widget:
The widget would be composed of controller, service, and template:
Controller
handles click events
triggers updates in the UI
Service
makes the actual REST calls to retrieve the data
the tree/tabs on the sidebar of your app would be linked to click events in the controller:
markup
<a ng-click="getContent(tabNumber)">tab label</a>
and here's where you display the tab content:
<div class="content-area">{{ contentArea }}</div>
controller
this function gets executed on click from the view:
...
$scope.getContent = function(tab) {
TabService.getContent(tab)
.then(function(response) {
// update the view
$scope.contentArea = response.data.content;
});
}
service
the service is then called from the controller's function:
app.service('TabService', function($http) {
return {
getContent: function(tab) {
return $http.get(....);
}
}
});
Here's a real basic working example of those pieces:
http://plnkr.co/edit/Tr5OIdwh1QLqOJFfr3xZ?p=preview
Related
I have started building a website using Angular, and have started to use ui-router along with ngAnimation for some sick animations! All was going well until I started adding external resources such as Facebook's Page Widget (https://developers.facebook.com/docs/plugins/page-plugin) and Google Maps API for some custom map styles.
The Issue
I have the Facebook widget on the home page, and it loads fine when you first access the URL as can be seen below (Have to block the name of client, totally not just bad design...):
Once I click on another page to load it in (using ui-view to dynamically bring in a view), for example contact view and go back to the home page (where the code is to display the widget), the widget will no longer load but will load the basic header tag that Facebook provide as can be seen below:
This also happens on the contact page when loading in Google Maps API. If I click on the contact page, there will just be a large white area on where the map should be, but if you had to actually refresh the page the map will load.
I am assuming that the issue is that the website is not able to send a request to either Facebook or Google when a view is loaded in, but can only happen when the page is actually refreshed or a new page load.
My Code
Okay so I'll explain the basics of how my application works along with code. I am rather new with Angular so if I have terrible code, please let me know.
Structure
This is a screen shot of my web apps structure. The main template file is index.html, and any view are within the views directory, which are loaded in using ui-router.
Main Angular applications code (app.js)
var app = angular.module('bmApp', ['ngAnimate', 'ui.router', 'ui.bootstrap'])
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('header', {
templateUrl: './assets/views/template.html',
})
.state('header.home', {
url: '/',
templateUrl: './assets/views/home.html',
controller: "HomeController"
})
.state('header.about', {
url: '/about',
templateUrl: './assets/views/about.html',
controller: "AboutController"
})
.state('header.work', {
url: '/work',
templateUrl: './assets/views/work.html',
controller: "WorkController"
})
.state('header.services', {
url: '/services',
templateUrl: './assets/views/services.html',
controller: "ServicesController"
})
.state('header.contact', {
url: '/contact',
templateUrl: './assets/views/contact.html',
controller: "ContactController"
});
$urlRouterProvider.otherwise('/'); //if no views are matched, just redirect back to home page.
})
I am using nested views to keep the header of the website static (not changing) and then loading in views under the header, with ngAnimate to make it look cool.
In my main template, index.html, I have a simple <div ui-view></div> to load in the view that is requested. I also have the required script to request Facebook's API just after the . My controllers do not actually currently have anything in them. I did have some vanilla JavaScript in some, but do not currently need it anymore. I also tried to wrap the code provided by Facebook in a function, and call it within the controller for the specific view but that just had the same results of having it within the body on the template (index.html) page.
Pulling my hair out at this, can't seem to find a solution. I hope I have explained my issue without missing out on anything. I appreciate any help you could give, would be so awesome.
Edit: I should have probably mentioned that my main template is index.html, I then inject the view in to /views/template.html AND THEN inject it into index.html. Not too sure if this is best practice, but felt like it would work well (I don't think that is the issue).
I will give you only half of an answer, but maybe it will point you to right direction. I also struggled with Google API in SPA application once. I had the same issue - after entering the contact page, a white box appeared instead of map. The solution turned out to be simple - I just needed to force map reload, when I entered contact sheet. This function was attached as onclick event to contact page link:
function reloadGoogleMap() {
if (counter == 0) {
document.getElementById('google-map').src += '';
counter++;
}
}
I used the counter to make sure it happens only once - you won't need it probably. With Angular, you might need to make sure the reload happens after element is rendered. Maybe a simple directive would work, however I didn't test that:
angular.module('your_directives_module')
.directive('mapLoaded', [mapLoadedDirective]);
function mapLoadedDirective() {
return {
restrict: 'A',
link: function (scope, elem, attrs) {
elem.on('load', function (event) {
// Force reload
elem.src += '';
});
}
};
}
It could be used like:
<!-- Div/iframe with google map -->
<div map-loaded></div>
Maybe Facebook API also has some simple way which you could use to force widget reload when the partial view is loaded, however I've never tried to use that with Angular.
Don't you have to wrap it in a $rootScope.$apply function in order to update your view?
$rootScope.$apply(function () {
// some code
});
I have a PhoneGap app, that has multiple html pages.
I use one controller, called AppController, that loads the data for the startup screen and default pages.
I added a new page to the navigation bar, which, when clicked & opened should load up data from the server with a php call, so it should only make a php call, when the page is shown.
This page uses the same controller as the rest of the app.
I am really new to AngularJS, so I might've programmed it in a bad way, but the rest of the data for the home page is loaded in the appController like this
myApp.controller('AppController', function ($scope, $timeout, sharedProperties) {
$scope.items= {};
$scope.items[item1] = {....} //LOADING UP the items collection
}
How can I hook up an "onload" event or something, that would only fire when the page is shown?
There are so many ways you could do that, one of them:
// inside your controller
angular.element(document).ready(function () {
// your code
});
another one:
<div ng-controller="myCtrl" ng-init="someinitfunc()"></div>
How about using ng-init() directive on your next page ?
In a Meteor app, a large collection containing 1000 records is published to the client. However users loading the {{loginButtons} will experience a 3-5 second lag as it fully renders only after all the large collection loads.
It appears that the div #login-buttons rendered by {{ loginButtons }} is rendered instantly on page load, but the div #login-dropdown-list is what's taking some time to start rendering. #login-dropodown-list template
The site is using Meteor 0.7.0.1 with Iron Router.
Update
Here's the template code for the accounts-ui-bootstrap-3 dropdown menu that take a few seconds to load after the rest of the page renders. It's just the basic template from the Meteor package, nothing special.
<ul class="nav navbar-nav navbar-right">
{{ loginButtons }}
</ul>
I used to think the problem is due to that this dropdown menu using the Meteor.users collection, so here's my Fast Render route.
FastRender.onAllRoutes(function(urlPath) {
this.subscribe(Meteor.users);
this.subscribe(users);
})
This does not seem to help with the problem. I also found out that Meteor.userId() is already defined when the dropdown menu is still not rendered. The dropdown menu only appears/renders at the point in time pointed to by the red arrow, which is the point where all the collections have loaded.
Furthermore, the div #login-buttons rendered by {{ loginButtons }} is rendered instantly on page load, but the div #login-dropdown-list is what's taking some time to start rendering.
Maybe it's how accounts-ui-bootstrap-3 handles the rendering?
You can use iron-router's waiton on specific routes and loadingTemplate properties to show the user a progress indicator while the subscription gets ready. As seen on https://github.com/EventedMind/iron-router#waiting-on-subscriptions-waiton
Router.configure({
layoutTemplate: 'layout',
notFoundTemplate: 'notFound',
loadingTemplate: 'loading'
});
Router.map(function () {
this.route('postShow', {
path: '/posts/:_id',
waitOn: function () {
return Meteor.subscribe('posts');
}
});
});
Also, https://atmosphere.meteor.com/package/fast-render is a third party package which integates into iron-router and sends down the initial data along with the template therefore making the page appear loaded with data instantly.
There is a good tutorial about this at http://meteorhacks.com/integrating-iron-router-based-apps-with-fast-render.html
I see two other packages that could help you :
Paginated subscription : https://atmosphere.meteor.com/package/paginated-subscription
The idea here would be to set a small limit for the initial load, and when the rest is loaded, change the limit to load the rest
Lazy subscription : https://atmosphere.meteor.com/package/lazy-subscription
The idea here is not to subscribe at all to your large collection in the first time ; only init it like that :
Post = new Meteor.Lazy('post', function() {
Meteor.subscribe('posts');
});
(it does not subscribe to anything)
And then when ready :
Template.some_template.get_posts = function() {
return Post().find({}); //Note that Post is now a function.
};
=> it doe subscribe
The second solution may seem more straight forward, but you can manage it way better with the first one.
I'm trying this (Using Jquery in Codeignitor app to update div every 10 seconds), it is working but it shows my whole view in my div
I work with hooks and I have a masterview where I load other views in it...
When I load my controller in to my div, it shows my masterview in my div with the view I request in my controller.
How can I just show the requested view without my masterview? Can you help me?
thnx Cheers
A quick solution that I've used in CI is to implement a check in the controller to see whether this is an AJAX request. If so, I load the view with data into a variable and return it to the browser as a chunk of html.
if($this->input->is_ajax_request())
{
$view_result = $this->load->view('child_view_for_div', $data,true);
echo($view_result);
}
else
{
//Return view with master template
}
In my home page i have an oulet watchListView i just want to replace the outlet with the view i am getting after clicking the link "/watch_lists".
This is the code for application Layout/template
<div id='main-outlet'>
{{outlet}}
<div class="watch-list-rght"><i class="icon-eye-open"></i> </div>
</div>
{{outlet watchListView}}
This is the code in application route
this.resource('watchList', { path: '/watch_lists' });
This is the code in WatchList router
Discourse.WatchListRoute = Discourse.Route.extend({
redirect: function() { Discourse.redirectIfLoginRequired(this); },
renderTemplate: function() {
this.render('watch_lists', { into: 'application', outlet: 'watchListView' });
}
});
I Just want to add view created after the ajax call in the application template
Can anyone point me out where i am wrong.
Your view is loading into the outlet as soon as you move into this route, it just has no data until populated by the ajax call. Put logging in the renderTemplate function and you'll see this.
I would make the ajax call in your route's setupController method, populating the controller with the results of that call so you can display those results in your view. See the documentation on this method for more info.
In the AJAX callback, you should be populating the controller with the results of that call and have handlebars in the template pointing to the data you want to display. It will load automatically when the Ajax call comes back and populates the controller:
setupController:function(controller){
someObj.AjaxCall(function(data){
controller.set('data',data);
}
}
You can watch controller properties in your watchList template with {{controller.data.property}} or {{{controller.data.htmlProperty}}} to render html.
If this is not helpful enough, post a jsfiddle that illustrates what you're currently doing.