lxjwlt's blog

AngularJS接收PHP变量数据

理想情况下,AngularJS处理的数据都是通过异步请求获取的,但有些情况下,由于开发环境的限制,只允许页面部分使用AngularJS。这类页面中的数据分为两种,一种是用于初始化页面的数据,一种是用于更新页面的数据。其中用于初始化的数据会决定页面初始化时的内容,如果这类数据也用异步请求获取,那么页面在呈现出来的时候可能是空白或错乱的。

注:理想状态指全站使用AngularJS,就算这种情况下,通过异步请求初始化数据也会造成页面在一开始空白或错乱,但我们监测$rootScope上的加载完成事件或监测$routeProvider的promise状态来判断是否初始化完成,在未完成前可以通过不显示页面或者给出一个loading的画面,来掩盖错乱的页面。但这些措施在局部使用AngularJS时是不太可能有效的,所以下面要讲诉的是妥协于非理想情况的解决方法。

通常这类页面在生成之前,后台就会初始化主要的数据,我们完全可以把这些后台数据利用起来,而不必再去发起异步请求。

以常见的PHP后台为应用场景,PHP生成页面前便会处理用户数据,用户数据中包含了用户权限,用户名,用户关注等等数据,这些数据在页面中也会被用到,比如用来决定用户面板的显隐:

// 假设%%为PHP模板输出语法
// 假设PHP的$member变量存储用户数据

% if !empty($member) %
<ul class="user-panel">
    <li>% $member.username %</li>

    % if $member[authority] > 1000 %
    <li>后台管理</li>
    % /if %

    <li>退出</li>
</ul>
% /if %

假设AngularJS中也用到用户数据,用于处理回复框的可输入状态,如果用户没有权限则不允许输入:

// $scope.memberData此时为undefined
<div class="reply-box">
    <textarea ng-disabled="memberData.authority > 1000"></textarea>
</div>

输入框是否可输入由memberData.authority决定,但memberData还没有初始化,所以我们先用后台数据初始化memberData。

ng-init初始化后台数据

通过ng-init指令在行内初始化AngularJS作用域:

<div class="reply-box" ng-init="memberData = % json_encode($member) %">
    <textarea ng-disabled="memberData.authority > 1000"></textarea>
</div>

上述调用PHP的json_encode函数将$member转换为JSON格式并输出到页面中,页面渲染结束后,AngularJS对该页面进行二次解析,在这过程中将这段JSON数据解析并赋值到$scope.memberData中。

但实际开发中,上面的写法会导致HTML的解析错误,因为JSON数据的格式是:

{"username": "lxjwlt", "authority": 9999}

JSON中带有双引号,也可能包含HTML语句,所以直接在HTML中打印出这类JSON要注意将引号和HTML特殊符号都进行HEX编码,PHP中的json_encode可以传入第二个参数,用于设置特殊字符的编码方式:

// JSON_HEX_TAG: 将 < 和 > 等符号编码为 \u003C 和 \u003E
// JSON_HEX_APOS: 将单引号转换为 \u0027
// JSON_HEX_QUOT: 将双引号转换为 \u0022

<div class="reply-box" 
     ng-init="memberData = % json_encode($member, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT) %">
    // ...
</div>

这种写法有两点不足: * 写在行内,要考虑引号等特殊符号的编码 * 公共数据不通用,如果页面中多处用到用户数据,那么就要多次初始化用户数据 * 数据初始化遍布页面各个地方,不利于管理

module.value全局数据存储

在AngularJS中,我们可以通过module.value来定义全局公用数据:

// 初始化angular应用
var app = angular.module('myApp', []);

// 全局数据
app.value('myData', {
    key_1: 'value_1',
    key_2: 'value_2'
});

利用AngularJS这一特性,我们可以将后台数据存储到AngularJS中的全局变量中:

<body ng-app="myData">
    <script>
    angular.module('myData')
        .value('memberData', % json_encode($member) %);
    </script>

    // ...
</body>

PHP模板会执行json_encode将$member数据打印到script标签中,而这段JSON数据在script标签中被当作是JS对象来对待,于是在AngularJS对页面的第二次解析时,直接将这段JS对象存入到全局变量memberData中。

于是,在我们在编写myApp模块时,可以在任意的控制器中获取这段全局数据 -- memberData:

angular.module('myApp')

    // 依赖注入memberData
    .controller('MyCtrl', function(memberData) {

        // 要在作用域中使用memberData,先赋值给$scope
        $scope.memberData = memberData;

    });

这样我们就能够使用用户数据:

<div class="reply-box">
    <textarea ng-disabled="memberData.authority > 1000"></textarea>
</div>

通过全局数据的方式,我们不再需要ng-init指令来初始化数据,也不用再考虑特殊符号的编码转换问题,而且这段数据可以全局复用。

总结

采用以上的方法,在AngularJS内部,我们能够获取任意后台数据,而我们只需花费数据解析的时间,从而节省下异步请求和第二次后台运行逻辑代码的时间。

参考